Viewify
A View is defined as any Module who’s Apex is an entity that returns a dispatch table that has been run through Viewify. This will give the module some default functionality it can work off of to create visualizations.
Container Views
Container Views are Views which are designed primarily as a means of layout. Examples of Container Views
include: PanelView
and GridView
.
Root Container Views
A Root Container View is like a Container View, in that its primary purpose is a means of layout. However,
Root Containers are required to attach Views to the DOM, and act as the controller for all descendants.
Examples of Root Container Views include RootView
and PopupView
.
Basic Preparation
For a View to work, the Viewify.js
script needs to be included on your Webpage. for example,
xGraph.WebViewer
automatically imports viewify into its pages. Unless you are writing your own Http server,
Viewify is probably already imported for you. To verify it has been imported, in a browser console, type
window.Viewify
. if it comes back with a function, then you’re good to go.
this.super
When the first command is sent to your view (typically the Setup command), it injects super
into the this
context. super
is used as a means to access shared View functionality, the Viewify base-class. A call to
this.Super
accepts two parameters: the command object com
, and a callback function fun
. The intended
use is to maintain Base functionality while extending a command.
For example, if you had your own setup process:
function Setup(com, fun) {
this.super(com, (err, cmd) => {
console.log('--MyView/Setup');
fun(null, com);
});
}
Schema
The View “Base Class” Utilizes the Setup
command, so even if your view doesn’t require it, any entity that
returns a View Dispatch Table should include Setup
in its schema.json
.
{
"Apex": {
"Entity": "MyView.js",
"$Setup": "Setup"
}
}
View Dispatch Table
A View Dispatch Table is the core functionality that allows the View “Base Class” to define common & shared functionality. A View Dispatch Table is created by passing your Entity class or dispatch table into Viewify
. The resulting object can be returned by the Entity’s closure and used as its Mod. In practice, Defining an Entity with a View Dispatch Table looks like the following:
(function MyView(){
class MyView {
MyCommand(com, fun) {
console.log('--MyView/MyCommand');
fun(null, com);
}
}
return Viewify(MyView, "3.0");
})()
Alternatively, if you aren’t using classes:
(function MyView(){
function MyCommand(com, fun) {
console.log('--MyView/MyCommand');
fun(null, com);
}
return Viewify({
MyCommand: MyCommand
}, "3.0");
})()
Setup
All Views receive a ‘Setup’ command before the first Render command is sent. This is a good place to setup the basic structure, or build elements that will not change.
Render
The Render
command is automatically sent to any entity who’s children have changed. Before the command is
dispatched to you, the array this.Vlt.viewDivs
is populated with jQuery wrapped elements that are your
View’s children. If the View is not a Container View, you can safely ignore, and not implement the Render
command.
A simple Render
command override:
function Render(com, fun) {
for(let i = 0; i < this.Vlt.viewDivs.length; i++) {
this.Vlt.div.append(this.Vlt.viewDivs[i]);
}
this.super(com, (err, cmd) => {
fun(null, com);
})
}
You’ll notice that in this case, the super call is at the end. This is because the purpose of the super call,
in this case, is to cascade the Render
command down the DOM tree.
DOMLoaded
DOMLoaded
is sent to you when all Views in the local root container have
completed loading. This refers, for example, to all views under a RootView
or PopupView
.
Just like in Render
, the purpose of the base class DOMLoaded
command is to cascade the command down the
DOM, so calling it after the view does what it needs to do is recommended.
function DOMLoaded(com, fun) {
alert('DOM Loaded!');
this.super(com, (err, cmd) => {
fun(null, com);
})
}
Viewify Changelog
3.5 (Released with xGraph 1.1)
this.authenticate
this.evoke
this.ascend
this.authenticate
this.Authenticate
returns a promise that resolves to an Authenticated version of any command object, given
as its first parameter. Usage of this command requires there be a module in the system that registers itself
as a CommandAuthenticator. (i.e. It sets window.CommandAuthenticator
to its Pid)
this.evoke
this.evoke
was amended to take an options object as its second parameter that get applied to the outgoing
Evoke Command. For more information, See version 3.4, where this.evoke
was introduced
this.ascend
this.ascend
was patched to disallow the options object parameter to overwrite the First parameter (Command
Name). This will not effect most uses of ascend, however sometimes resulted in a bug. For more info on
this.ascend
see version 3.0 and 3.3
3.4 (Released with xGraph 1.0)
this.asuper
this.genModuleAsync
this.cdnImportCSS
this.id
this.evoke
this.asuper
this.asuper(com: object): Promise<com: object || throws [err: any, cmd: object]>
is an asynchronous
wrapper for this.super(com: object, fun: (err: any, cmd: object) => void): void
Example
(function MyView() {
class MyView {
Setup(com, fun) {
// before base class setup
this.super(com, (err, cmd) => {
// after base class setup
});
}
}
return Viewify(MyView, '3.4');
})();
can be written asynchronously as
(function MyView() {
class MyView {
async Setup(com, fun) {
// before base class setup
com = await this.asuper(com);
// after base class setup
}
}
return Viewify(MyView, '3.4');
})();
this.genModuleAsync
this.genModuleAsync(modDef: {Module: string, Par: object, Source?: string}): Promise<apx: string || throws err: any>
is an asynchronous wrapper for
this.genModule(modDef: {Module: string, Par: object, Source?: string}, fun: (err: any, apx: string) => void): void
.
Example
(function MyView() {
class MyView {
CreateThing(com, fun) {
this.genModule({
Module: 'Custom.Module',
Par: {
Link: this.Par.Link
}
}, function(err, apx) {
com.Apx = apx;
fun(null, com);
});
}
}
return Viewify(MyView, '3.4');
})();
can be written asynchronously as
(function MyView() {
class MyView {
async CreateThing(com, fun) {
let apx = await this.genModuleAsync({
Module: 'Custom.Module',
Par: {
Link: this.Par.Link
}
});
com.Apx = apx;
fun(null, com);
}
}
return Viewify(MyView, '3.4');
})();
this.cdnImportCSS
this.cdnImportCSS(url: string): void
will Import a stylesheet from a CDN. This is useful generally only in
development.
Example
this.cdnImportCss('//fonts.googleapis.com/icon?family=Material+Icons');
this.id
this.id(id: string): string
is a function that creates GUIDs based on the provided id
and your View’s
Pid
. The same this.id
call in the same entity instance will always return the same GUID. This allows for
a view to declare simple IDs like id="${this.id('button')}"
in the DOM and avoid conflicts.
Example
(function MyView() {
class MyView {
Render(com, fun) {
this.Vlt.div.append($(`
<p id="${this.id('paragraphOne')}">
this paragraph's ID is actualy a GUID,
however within this entity, we can get
that GUID again, with this.id.
</p>
<p id="${this.id('paragraphTwo')}">
So your easy to remember IDs can exist
in the DOM's global scope without
conflict!
</p>
`));
let paragraphOne = $(`#${this.id('paraGraphOne')}`);
let paragraphTwo = $(`#${this.id('paraGraphTwo')}`);
fun(null, com);
}
}
return Viewify(MyView, '3.4');
})();
this.evoke
this.evoke(pid: string): Promise<void>
will send an evoke to a pid, and take action on the response. This
is used to standardize the way that a module declares its visualization.
Example
In a server side module
(function Serverside() {
class Serverside {
Evoke(com, fun) {
com.Type = "View";
com.Container = 'xGraph.Views.Popup';
com.View = "xGraph.Views.TableView";
com.Par = {
Serverside: this.Par.Pid
};
fun(null, com);
}
}
return { dispatch: Serverside.prototype };
})();
On the Browser, to open Serverside
’s Visualization
(function BrowserButton() {
class BrowserButton {
async Click(com, fun) { //example button click command
await this.Evoke(this.Par.ServersidePid);
fun(null, com);
}
}
return { dispatch: BrowserButton.prototype };
})();
3.3
- updated ascend functionality
- debugger logging
- Cleanup / Destroy
Update ascend Funtionality
If ascend throws an error, it will now be in the form of [err, cmd]
such that you can still pull out the
original com, even in the event of a rejection.
Example
try {
let results = await this.ascend('CommandToFail', {}, this.Par.Somewhere);
} catch([err, cmd]) {
// cmd = { Cmd: 'CommandToFail' }
log.e(err);
}
debugger loggging
If URL Parameters are supported by the server beingg used, you can append ?debug
or &debug
to display the
full Viewify command tree in the browser console.
Example
http://localhost:80/login?debug
http://localhost:80/View?item=28884737749&debug
Cleanup / Destroy
Sending a Destroy
command to a View will cause it to remove itself from the DOM and the entity cache,
causing it to be garbage collected.
As a result, The Cleanup
command will be sent to the module, before it is destroyed, in case it needs to
perform some actions on exit.
As well, the parent of the destroyed view will be sent a Render command with an updated Vlt.views
and
Vlt.viewDivs
3.2
- Drag and Drop
Drag and Drop
AttachDragListener
will make an element that you specify draggable. along with it, you can set some other
data to be tied to the element / drop event (when it fires).
Example:
this.super({
Cmd: 'AttachDragListener',
To: myDraggableElement, //Native element, not jquery.
Datatype: 'ItemList',
Data: {
Items: [5, 8, 2, 4, 1, 6, 8]
}
});
To create an area where you can drop items, add the dropArea
class to an element. the View that the Element
belongs to will then get a Drop
Event Command sent to it. the com will look like the following:
com = {
Cmd: 'Drop',
Data: {
Items: [5, 8, 2, 4, 1, 6, 8]
},
DropArea: Native_HTML_Element,
Datatype: 'ItemList',
PageX: 463,
PageY: 285,
DivX: 463,
DivY: 247
};
3.1
- Version Specificty
- Classes
- IDs
Version Specificty
Viewify
function now accepts a second parameter, version
as type string
that is the semantic version
number the view was built in compliance with.
Example
(function MyView() {
class MyView {
//Contents
}
return Viewify(MyView, '3.0');
})();
Classes
Each View’s userland div, this.Vlt.div
, is now given a class name equal to the last token in the dot
separated this.Par.Module
.
IDs
Each View, in its Config Par, Can now declared and ID
key. If present, the value of the key will be used as
the value of the ID
attribute on this.Vlt.div
.
3.0
this.super
this.ascend
this.super
this.super(com, fun)
is a way to pass commands to your view’s base class. For example, you may define your
own setup function, however Viewify’s own setup still needs to run. in this case, you would call this.super
in your setup function.
Example
(function MyView() {
class MyView {
Setup(com, fun) {
// before base class setup
this.super(com, (err, cmd) => {
// after base class setup
});
}
}
return Viewify(MyView);
})();
this.ascend
this.ascend
is an asyncronous version of this.send
with slightly different parameters. the signature is
this.ascend(command: string, options: object, destination: string): Promise<com: object || throws err: any>
Example
async asyncFunction() {
try {
let data = await this.ascend('GetData', {
Type: 'Volume/Time'
}, this.Par.DataSource);
// data is the returned com
} catch (err) {
log.w(err)
}
}