Creating Dojo Widgets with Inline Templates

By on February 13, 2014 11:58 am

Many Dojo widgets make use of client-side templating for generating the UI. Template HTML files are brought in to the page with dojo/text, parsed and converted to DOM nodes, and placed on the page, allowing our code to make substitutions, instantiate widgets within the template, and hook up events and attach points. However, we may find ourselves dealing with markup generated on the server and delivered on the page that we want to enhance with Dojo. Luckily, it is quite simple to make a custom widget that uses its source node as the template, allowing us to use markup already on the page as the template for our widget.

This is an updated version of the post Creating Dojo Widgets with Inline Templates, published in 2008.

Widgets can easily take advantage of existing source nodes to define how they might end up rendering. They might use the source nodes to define a data set. They could be widgets that manage a number of child widgets as is done with the various Dijit Layout widgets. However, under normal circumstances, a widget’s source node is replaced by the nodes generated from its template or the original source nodes are moved to a container node within your template. What we are looking for is a way to define a widget that can encapsulate behaviors and data, provides for dynamic template generation, and retains the utility of data-dojo-attach-point and data-dojo-attach-event from the templating
system.

The good news is that it is very easy to do. Let’s look at a normal templated widget declaration:

define([
    'dojo/_base/declare',
    'dijit/_WidgetBase',
    'dijit/_TemplatedMixin'
], function (declare, _WidgetBase, _TemplatedMixin) {
    return declare([_WidgetBase, _TemplatedMixin], {
        templateString: '<div><div data-dojo-attach-point="container"></div><div data-dojo-attach-event="mouseover:mouseOver">Go</div></div>',
        mouseOver: function () {
            console.log('mouseover');
        }
    });
});

This is a simple widget that does not do much. As you can see, it will end up creating a div containing two child divs at its declared location on the page. The first of these children has a data-dojo-attach-point attribute, which will create a reference to this DOM node at this.container in the widget instance. the second div has a data-dojo-attach-event attribute which will connect that DOM node’s mouseover event to the widget’s this.mouseOver method.

There are numerous examples of using templates inside of Dijit, but that’s not what we want as it is too static. Our templates will be defined as part of the instance declaration instead of the widget’s JavaScript declaration. To accomplish this goal, we simply need to declare a new widget that extends from _WidgetBase and mixes in _AttachMixin and _WidgetsInTemplateMixin. The _AttachMixin module is new with Dojo 1.9 and separates the DOM node attribute parsing functionality from template processing, enabling widgets to automatically process custom node attributes used by Dijit templates of arbitrary HTML (as opposed to just pre-created templates). This enables enhancement and widgetization of existing HTML (similar to directives in AngularJS).

By extending from _WidgetBase, we get the expected widget life cycle, getters/setters, and overall structure of a common dijit. Adding the _AttachMixin will automatically set the widget’s domNode to the srcNodeRef, the DOM node that contains the data-dojo-type attribute and the root node of our widget. This will also parse the elements for data-dojo-attach-point and data-dojo-attach-event attributes and set them up accordingly. Finally, mixing in _WidgetsInTemplateMixin adds support for parsing additional declarative child widgets within our srcNodeRef.

It is worth mentioning that this approach does not make variable substitution available to us, meaning that strings like ${stringToReplace} would not be interpolated. This may not be an issue, however, as variable replacement can easily be taken care of by the server prior to delivering the content to the client.

As an example, let’s say we have the following static markup in our page:

<div data-dojo-type="my/Widget">
    <div data-dojo-attach-point="textNode"></div>
    <button data-dojo-type="dijit/form/Button" data-dojo-attach-event="click:go">Go!</button>
    <button data-dojo-type="dijit/form/Button" data-dojo-attach-event="click:stop">Stop!</button>
</div>

And the widget code would look like this:

define([
    'dojo/_base/declare',
    'dijit/_WidgetBase',
    'dijit/_AttachMixin',
    'dijit/_WidgetsInTemplateMixin',
    // used by the template
    'dijit/form/Button'
], function (declare, _WidgetBase, _AttachMixin, _WidgetsInTemplateMixin) {
    return declare([_WidgetBase, _AttachMixin, _WidgetsInTemplateMixin], {
        go: function () {
            this._setText('Go! button clicked');
        },

        stop: function () {
            this._setText('Stop! button clicked');
        },

        _setText: function (text) {
            var node = document.createTextNode(text);
            this.textNode.innerHTML = '';
            this.textNode.appendChild(node);
        }
    });
});

The instance of my/Widget will have a reference to the div at this.textNode, and the buttons, when clicked will call the widget’s go() and stop() methods, respectively. As you can see, there isn’t really any difference between this and a normal template provided by an inline string or in a template HTML file. However, it’s easy for complex servers to generate a lot of HTML and then wrap it in a my/Widget to maintain it.

Experiment with a live demo at JSFiddle (to run in the JSFiddle environment this demo uses the first parameter to dojo/declare to declare a named global constructor – in normal development you would encapsulate the widget in its own module as shown above).

Comments

  • Pingback: Creating Dojo Widgets with Inline Templates | Blog | SitePen()

  • aarkhipov1991

    Dijit templates are in need of data-binding or, at least, control structures like “foreach”, “if” and others. Without it templates is not very useful. What you think about it?

  • jausel

    This example seems to be lacking a data-dojo-attach-point for a widget. The example only sets an attach point for a div. What do you need to do to set an attach point for one of the buttons? When I tried to do this on the JSFiddle the attach point doesn’t get set.

  • When you set a data-dojo-attach-point on a widget reference, it becomes a reference to the widget rather than the domNode for that button. That said, it does get set.

  • This and more is in the plan for Dojo 2.

  • Gabriel Aszalos

    Dylan, I’ve implemented a basic one-way binding for Dojo. It’s currently a private repo as I have created it for the company I work for. I also have an experimental repeater. Would you be interested to see? It’s is fully covered by tests and seems to work very well with Stateful (or any observable) models.

    I’ve tried to get in touch but I haven’t seen any contact information on how to reach you. Maybe I’ll see you at London Ajax in July.

  • Shravan Shetty

    Hey Gabriel, I am in need of this. If possible Can u share this over (shravan.rvce@gmail.com)