Model

A Model object is used for uni-directional or bi-directional data synchronization with widgets.

Purpose

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.

Usage

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.

RUN DEMO

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");
   }
);

Examples

The following examples illustrate various ways of model to widget binding.

Widget Loader

The binding is established during activation of a widget by means of the load method.

RUN DEMO

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);
});

Widget Constructor

A widget can be created and then activated dynamically in a program code.

RUN DEMO

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);
});

Infinite Recursion

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:

  1. Somehow, the model property value is changed for the string that contains “/” (slash).

  2. The string must be synced with the Output value. But the Output widget will escape the “/” character by substituting it with the “&x2F;” sequence.

  3. Since the Output value has changed, it must be synced with the widget property.

  4. 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.

  5. 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)
}]