A Model object is used for uni-directional or bi-directional data synchronization with widgets.
In this document:
Data model based on the constructor from the dojox/mvc/getStateful
module is widely used
due to the following features.
It contains the get
and set
methods to work with named properties.
The model can synchronize an object with widgets by means of the dojox/mvc/at
method.
The watch
method allows tracking an object property. It calls a custom callback method once the property
is changed. The application can deactivate the method at any time.
Generally, the method is used the same way as the Mozilla JavaScript’s watch
method.
Get more details about the stateful models at the Dojo site, for example, read the dojo/Stateful document.
You may change data of the model using the standard set
method.
This method accepts two arguments: the name of the property being changed
and a new value. See details in Base Properties and Methods.
Using the business controls of the dojox/mvc
group, you can bind data
with widgets. Such binding creates a bidirectional or a unidirectional
mechanism for updating the bound widget and the data.
Furthermore, using dojox/mvc
you can synchronize changes in several
widgets. If they are bound with the same model, changes in one widget
will be applied to all widgets.
The simplest way of binding a model and a widget is to use the at
method of the dojox/mvc/at
module. This method accepts two arguments:
First argument must specify the parent structure of the property stored in the model. The following examples help to understand:
If the property is at the root of the model, the model itself is specified, that is at(model, property)
.
If the property is a part of a structure or sub-structure, the structure or substructure in the model is specified,
that is at(model.structure, property)
, or at(model.structure.sub-structure, property)
.
Second argument is the property to be synced.
In addition to direct model to widget binding, using the
at
method you can specify intermediate formatting of data. To do
this, you need first to set data binding with help of the
direction
method and then set data transformation methods in the
object passed to the transform
method.
The direction
method sets one of the following sync modes:
Bidirectional, for example:
cpu: at('model', 'cpu')
Unidirectional from model to widget, for example:
cpu: at('model', 'cpu').direction(at.from)
Unidirectional from widget to model, for example:
cpu: at('model', 'cpu').direction(at.to)
The transform
method accepts the following arguments:
format - is a method that transforms data from the model into data that will be passed to a widget.
parse - is a method that transforms data from a widget into data that will be passed to the model.
Please note that it is not necessary to define the both methods. If the
data can change only in the model and cannot change in the widget,
it is sufficient to define only the format
method as in the following example.
require(["aps/load", "dojox/mvc/getStateful", "dojox/mvc/at", "aps/ready!"],
function(load, getStateful, at) {
var model = getStateful({
"platform":
{
"arch": "i386",
"os":
{
"name": "Linux",
"version": "centos-6"
}
},
"cpu": 76
});
load([[ "aps/Output", {
content: "CPU: ${cpu}",
cpu: at(model, 'cpu').
direction(at.from).
transform( {
format : function (value) {
return value + "%";
}
})
}]], "output");
}
);
The following examples illustrate various ways of model to widget binding.
The binding is established during activation of a widget by means of the load
method.
require(["dojox/mvc/getStateful", "dojox/mvc/at", "aps/load", "aps/ready!"],
function(getStateful, at, load){
var model = getStateful({
"platform": {
"arch": "i386",
"os": {
"name": "Linux",
"version": "centos-6"
}
},
"num": 0
});
load([[ "aps/Output", {
content: "This is ${name} - ${version}",
name: at(model, 'num'),
version: at(model.platform.os, 'version') }
]], "output");
setInterval(function(){ model.set("num", model.num+1); }, 1000);
});
A widget can be created and then activated dynamically in a program code.
require(["aps/Output", "dojox/mvc/getStateful", "dojox/mvc/at", "aps/ready!"],
function(Output, getStateful, at){
var model = getStateful({
"platform":
{
"arch": "i386",
"os":
{
"name": "Linux",
"version": "centos-6"
}
},
"num": 0
});
var out = new Output({
content: "This is ${name} - ${version}",
name: at(model, 'num'),
version: at(model.platform.os, 'version')
}, "output");
out.startup();
setInterval(function(){ model.set("num", model.num+1); }, 1000);
});
Bidirectional synchronization between a model and a widget may lead to infinite recursion. This can happen when a widget automatically changes the value synced with a model property.
Currently, the widgets escape special HTML characters if they exist in a string assigned to some
of their properties.
It means they substitute such a character with the “&entity_name;” or “&entity_number;” sequence
in accordance with the HTML codes table .
Suppose, there is a bidirectional synchronization between the value
of an aps/Output
widget and a model string property. This case is prone to the following scenario:
Somehow, the model property value is changed for the string that contains “/” (slash).
The string must be synced with the Output value. But the Output widget will escape the “/” character by substituting it with the “&x2F;” sequence.
Since the Output value has changed, it must be synced with the widget property.
The model accepts the string containing “&x2F;”. Once the property is changed, the model sends its value to all widgets that are bound to the model, including the widget that is the source of the change.
The widget notices “&” in the input string and substitutes this character with “&”. Then, the widget tries to sync it with the model.
The synchronization process cycles until the respective stack overflows.
For this reason, to have more robust code, use the unidirectional synchronization wherever possible, for example:
["aps/Output", {
label: "OS",
value: at(model, 'text').direction(at.from)
}]