James Thomas

Notes on JavaScript

Creating Todo MVC in Dojo - Part 2: Views

In the previous article, we looked at defining our application Model using the Dojo MVC package. The model contained a list of todo tasks, each with a description and finished state, along with composite values representing the total completed and remaining task counts.

Dojo’s MVC package provides a series of widgets (Group, Output, Repeat, Generate) that assist the rendering of model attributes in our View, which automatically update when model values change. We’re going to look at using these widgets to build our TodoMVC Application View…

Defining a View template

For a long time, Dojo has had excellent support for creating widgets with HTML templates using modules from the Dijit package. Defining a new widget simply requires inheriting from a base class (_WidgetBase), adding in mixins for template support (_TemplatedMixin & _WidgetsInTemplateMixin) and providing a HTML template file that will be automatically rendered in the page by the Dojo parser. In our application, we’ve created a new templated widget todo.app, present in app.js along with a template file app.html.

The basic outline of this module is shown below.

Templated TodoMVC WidgetSource Link
1
2
3
4
5
6
7
8
9
10
11
12
define(["dojo/_base/declare",
        // Parent classes
        "dijit/_WidgetBase", "dijit/_TemplatedMixin", "dijit/_WidgetsInTemplateMixin",
        // Widget template
        "dojo/text!./app.html",
        ...
    function(declare, _WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, template) {
        return declare("todo.app", [_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin], {
            templateString: template,
        });
    }
);

Defining our application as a templated widget lets us annotate HTML elements, using the data-dojo-type attribute, in the application page to be automatically instantiated into corresponding widgets at runtime by the Dojo parser. When this occurs, the view template will replace the annotated HTML element in the page and any child widgets in the view will be recursively created.

The HTML snippet below shows how we load Dojo, the parser, our application and turn on the automatic parsing on load to render our Todo widget within the page.

Loading TodoMVC App Within The PageSource Link
1
2
3
4
5
6
<script data-dojo-config="async:true, parseOnLoad:true, paths:{'todo':'../../todo'}, 
    deps:['dojo/parser', 'todo/app']" src="./js/dtk/dojo/dojo.js"></script>

...

<div class="content" data-dojo-type="todo.app"></div>

This templated widget is rendered within the page and represents our view. The view has three main tasks:

** Display the list of todo items * Allow a user to enter more tasks * Show statistics for the number of completed and remaining tasks.

We’ll look at each individually to see how we implemented those features using the Dojo MVC package.

Displaying Todo Tasks

We’ve used the <ul> element to represent the container for our todo tasks, each task being an <li> element with the same HTML template populated with different values. The content for each task and the number of tasks needs to be dynamically generated based upon the values in the model.

Dojo MVC provides a widget for this pattern, dojox.mvc.Repeat, letting the user specify an array in the model for binding to with a HTML template to be repeated for each element.

The HTML template for the repeating todo tasks is shown below.

Rendering Todo TasksSource Link
1
2
3
4
5
6
7
8
9
10
11
<ul class="todo-list" data-dojo-attach-point="todo_list" data-dojo-type="dojox.mvc.Repeat" data-dojo-props="ref: this.model.todos, exprchar: '#'">
    <li data-dojo-type="dojox.mvc.Group" data-dojo-props="ref: '#{this.index}'">
        <div class="todo">
            <input class="check" data-dojo-type="todo.form.CheckBox" data-dojo-props='ref: "isDone"'>
            <div class="todo-content dijitInline" data-dojo-type="dijit.InlineEditBox"
                data-dojo-props='ref: "todo_text", editor:"dijit.form.TextBox", autosave:true, width:"420px", style:"width:420px;"'></div>
            <span class="todo-destroy" data-model-id="#{this.index}">
            </span>
        </div>
    </li>
</ul>

Repeating View Templates

In the template, the containing <ul> element is registered as the dojox.mvc.Repeat widget using the data-dojo-type attribute. Using the data-dojo-props attribute, we can pass widget parameters to the newly created widget when it’s instantiated by the parser.

The first parameter, ref, provides a variable name for the model attribute to bind to. We’re using the model available on the widget instance, “this.model”, with the “todos” attribute, which contains an array of task objects. The second parameter, exprchar, is the character to use for substitution expressions in declarative child widget attributes. By default, this value is the ‘$’ character. As we’re using the declarative programming style, these expressions would be confused with normal template value substitutions and we use the ‘#’ character instead.

Under the <ul> element, we have the HTML template for each todo task, represented by the <li> element. Here we are using the dojox.mvc.Group widget, which provides the parent model context for any child widgets which use substituion expressions and references to access model values. Registering the parent context as an individual item within the model “todos” array is achieved by using the “#{this.index}” reference, this will be automatically incremented by the parent Repeat widget as it iterates through the model array.

Binding UI Controls

Each todo task has three UI controls, the task text (either as a read-only value or an inline edit box for modification), a checkbox representing the completed state of the widget and an icon to allow the removal of the task. Our chosen UI controls (InlineEditBox & CheckBox) are bound to model values by providing a “ref” attribute with the attribute name. These widgets will automatically be populated with the current model values during rendering.

This binding between the view and the model is bi-directional, changes to the model will automatically propagate to the view widgets and changes to the view widgets will flow back to the model.

When a user wants to remove a task, the “click” event generated by the icon in the view will be intercepted by the Controller. Allowing the controller to determine which todo task to remove from the model is provided by setting the current item index as a custom parameter on the HTML element. We will be looking at the code to perform the removal in a later article.

Showing Completed & Remaining Counts

Along with the todo tasks, we need to display stats for the number of completed and remaining tasks. These attributes are composite model values, being automatically calculated from other model attributes. The displayed values need to be updated live as the other model attributes change but won’t be directly modifable by the user. The code snippet below shows the HTML template in the view for this component.

Showing Task StatsSource Link
1
2
3
4
5
6
7
8
9
10
11
12
13
<div class="todo-stats" data-dojo-attach-point="todo_stats">
    <span class="todo-count">
        <span data-dojo-type="dojox.mvc.Output" data-dojo-props="ref: this.model.incomplete" class="number"></span>
        <span class="word">items</span> left.
    </span>
    <span class="todo-clear">
        <a href="#" data-dojo-attach-event="onclick:removeCompletedItems">
            Clear
            <span class="number-done" data-dojo-type="dojox.mvc.Output" data-dojo-props="ref: this.model.complete" ></span>
            completed items
        </a>
    </span>
</div>

We’ve used simple HTML elements to display the values, rather than Dijit widgets, due to the lack of user interaction needed. Binding a HTML element to a Model property can be achieved using the dojox.mvc.Output widget. Again, we use the “ref” attribute to specify which model property the “innerHTML” value on the HTML element should be linked with.

Adding New Tasks

Finally, we need to let the user add new tasks. Using a normal HTML input field, we connect the “onkeypress” event to an internal event handler to allow the app to access the new tasks.

Adding New TasksSource Link
1
2
3
4
<div class="create-todo">
    <input class="new-todo" data-dojo-attach-event="onkeypress:onKeyPress" placeholder="What needs to be done?" type="text"/>
    <span class="ui-tooltip-top" style="display:none;">Press Enter to save this task</span>
</div>

The application controller is responsible for handling the generated events, retrieving the new tasks and adding them into the model. We’ll be looking at exactly how this works in the next article. When new tasks are added, the task list in the view will be automatically re-rendered.

Conclusion

Following on from the first article, we’ve now looked at how the View component of our MVC application works. Using Dojo MVC widgets, we’ve been able to bind simple HTML elements and full Dijit widgets to show model values. This dynamic binding allows Model updates to automatically flow through to the View controls. Secondly, any user modified values automatically update the Model.

The dojox.mvc.Output widget was used to display Model attributes are read-only values in normal HTML elements. We also used the dojox.mvc.Repeat and dojox.mvc.Group widgets to generate a repeated view template for the todo tasks. Connecting user events from the view to the Controller used Dojo’s templated widget mixins.

In the final article, we’ll look at the last component the MVC pattern, Controllers. This includes adding new tasks to the Model, removing individual and completed tasks along with controlling the view components being displayed.

Comments