James Thomas

Notes on JavaScript

Creating Todo MVC in Dojo - Part 1: Models

In this first article, we going to look at using DojoX MVC to define our application Models, showing the use of the new StatefulModel class. Later in the series, we’ll look at binding our defined Model to a View template, rendering the HTML output and hooking into user events.

Introducing StatefulModels

DojoX MVC uses a specific class for representing Models in the MVC pattern, StatefulModel. By using or extending this class, applications have access to a native JavaScript data model that integrates with all the classes under the MVC package. StatefulModel extends the Dojo Stateful class, introduced in Dojo 1.6 to provide a way to monitor widget property changes, to support more complex behaviour, such as binding to and updating View component, validating of model values and much more.

StatefulModels are instantiated from a plain JavaScript object, representing the initial data structure for the model, or even from a Dojo Store (with support for both synchronous and asynchronous results). To assist the conversion of complex JavaScript objects, a factory function is provided that will traverse the source data, recursively converting all data properties to use the StatefulModel class.

Further details on the StatefulModel class can be found here.

Defining a Model

Reviewing the TodoMVC specification, our application Model needs to contain a list of todo tasks, each one containing a description and that task’s completed state. The task’s description will be provided by the user and can later be modified. As the task is completed, the task’s internal state must be updated. Representing the todo tasks as an array of objects will allow binding to a Repeat View, having a HTML template rendered for each task in the Model.

Looking at the application, there’s two additional properties in the View that are derived from other Model attributes, counts for the completed and remaining tasks. To allow automatic binding of these values into our View template, we’re going to represent these directly in our model. Later on, we’ll set up binding between Model values to allow those composite values to be calculated automatically.

1
2
3
4
5
6
7
8
9
10
11
12
var data = {
    todos: [
        {
            text: "Must write this blog article"
            isDone: false
        }
    ],
    remaining: 0,
    complete: 0
}

var model = dojox.mvc.newStatefulModel({data: data})

Once we’ve defined our Model as a native JavaScript object, we can use the StatefulModel factory to perform the conversion to StatefulModel classes, as shown in the sample above. The result of this function is our application Model, which we can then bind to our View and interact with via the Controller.

The StatefulModel class supports any native JavaScript type, we’re using strings for the description, booleans for the completed state and numbers for the computed totals.

Binding to Model changes

Once you’ve defined a model, you’ll want to be notified of any attribute changes. Using Dojo Stateful’s “watch” method, we can register a function to be executed whenever that model’s attributes are modified using the getter & setter pattern.

1
2
model.watch("attribute", function (attribute, new_value, old_value) {
});

The dojox.mvc.Bind module extends this functionality to cover two common patterns, listening to changes on a series of attributes and binding model properties together. The “bindInputs” function allows an array of model attributes to be passed in, executing a callback whenever any of the attributes change.

1
2
mvc.bindInputs([model.first_attr, model.second_attr, ...], function (attribute, new_value, old_value) {
});

More usefully, the “bind” function sets up a one way coupling between a source and destination model attribute. Whenever the source attribute value changes, the destination model attribute will be automatically updated with the same result. If you want to transform the source value before it’s updated in the destination model, an optional function callback can be passed in. This will be called with the new attribute value and the return value used to update the destination model.

1
2
3
mvc.bind(source_model, "attr", dest_model, "attr", function (attribute, new_value, old_value) {
    // Return transformed value to update dest_model with
});

Creating composite model values

In the TodoMVC application our model has two attributes, “remaining” & “completed”, that need to computed automatically from other model values.

Calculating the “completed” total is performed by looping through the “todos” array and counting the tasks which have an “isDone” attribute as true. Whenever a task’s “isDone” value changes we want to re-calculate and update the “completed” total. Whenever a new task is added, we automatically bind the new item to our calculating function, which updates the composite value with the new count if any of our model tasks are modified.

1
2
3
4
5
6
7
8
9
bindIsDone: function (item) {
    mvc.bindInputs([item.isDone], lang.hitch(this, "updateTotalItemsLeft"));
},

updateTotalItemsLeft: function () {
    this.remaining.set("value", array.filter(this.todos, function (item) {
        return item && !item.isDone.value;
    }).length);
}

Once we have the “completed” total, the “remaining” attribute can be easily calculated if we know the total number of tasks. Using the “bind” method ensures this happens automatically, using optional transform function to create the new composite value.

1
2
3
mvc.bind(this.remaining, "value", this.complete, "value", lang.hitch(this, function (value) {
    return this.todos.get("length") - value;
}));

Extending StatefulModel

Defining our Model requires application code to declare the composite (completed, remaining) attributes, bind to changes on the current (and future) todo tasks and deal with persisting data to local storage. Rather than having this code in the application controller, we can extend the StatefulModel class to encapsulate all this logic. Using Dojo’s Declare module, we can create a new class which extends StatefulModel, allowing us to set the initial model properties as class attributes rather than having to call the dojox.mvc.newStatefulModel() factory manually. The skeleton outline for our StatefulModel extension is shown below, we’ve removed some internal methods for brevity.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
define(["dojo/_base/declare", "dojox/mvc/StatefulModel", "todo/store/LocalStorage", "dojox/mvc"],
    function(declare, StatefulModel, LocalStorage, mvc) {

    return declare([StatefulModel], {
        data: {
            id: "local_storage_todos",
            todos : [],
            remaining: 0,
            complete: 0
        },

        store: new LocalStorage(),

        constructor: function () {
            var data = this.store.get(this.data.id) || this.data;
            this._createModel(data);

            this.setUpModelBinding();
            this.updateTotalItemsLeft();
        },

        setUpModelBinding: function () {
            mvc.bind(this.incomplete, "value", this.complete, "value", lang.hitch(this, function (value) {
                return this.todos.get("length") - value;
            }));
            array.forEach(this.todos, lang.hitch(this, "bindIsDone"));
            this.todos.watch(lang.hitch(this, "onTodosModelChange"));
        },

        updateTotalItemsLeft: function () {
            this.incomplete.set("value", array.filter(this.todos, function (item) {
                return item && !item.isDone.value;
            }).length);
        }
    });
);

The internal “data” and “store” attributes correspond to properties on the base StatefulModel class. StatefulModel’s constructor will automatically transform raw JavaScript objects, set via the “data” property, into the corresponding StatefulModels.

Unfortunately, it doesn’t contain code for automatically populating the initial model from the “store” property, this is handled in the “dojox.mvc.newStatefulModel” factory. Therefore, we manually construct the initial model values from the store, if available, and re-call the “_createModel” function in our constructor.

Once the model has been instantiated, we can declare and initialise our composite model bindings as well as binding to the initial todo tasks pulled from persistent storage.

You can see the source code for the final version of the TodoModel class here.

Conclusion

In this first article, we’ve reviewed how to use DojoX MVC’s StatefulModel class to create application Models that we can later bind to Views within the application. Using the StatefulModel class, we can bind to model properties, having callbacks executed when one or more attribute values changed.

Taking this further, we can bind model properties together, creating dynamic models whose properties automatically update as others change. Combining this functionality, we defined composite model attributes for the “completed” and “remaining” properties that were automatically calculated as the todo tasks’ state changed, wrapping this code within an extension of the StatefulModel class.

Next time, we’ll be looking at another component of the MVC pattern, Views. Using the TodoMVC application as our example, we’ll show how to define View templates, bind our Model with the View to generate the application’s HTML and ensure user interactions with the todo tasks automatically update the Model.

Comments