Creating Template-based Widgets

In this tutorial, you'll learn about the importance of Dijit's _TemplatedMixin mixin, and how to use templates to quickly create your own custom widgets.

Getting Started

If you are not already familiar with the basics of creating widgets with Dijit you will want to first read the Understanding _WidgetBase tutorial. The Creating a custom widget tutorial and Writing Your Own Widget guide will also help you learn to create widgets.

Dijit's _WidgetBase provides a fantastic foundation for creating widgets, but the _TemplatedMixin mixin is where Dijit really shines. With _TemplatedMixin and _WidgetsInTemplateMixin, you can quickly create widgets that are highly maintainable, quickly modifiable and easy to manipulate.

The basic concept of _TemplatedMixin is simple enough: it allows a developer to create a small HTML file that has a few small extensions, and loads this HTML file as a string at run-time (or inlined during the build process) for re-use by all instances of the templated widget.

Let's walk through what _TemplatedMixin defines (and why), and then build a simple widget from scratch using its functionality.

Note that _TemplatedMixin is intended to be used as a mixin, and not directly inherited from. In class-based parlance, that means that is more like an interface than a class (although with JavaScript, the difference between the two is muddied). See the Dojo Declare Tutorial for more information on how classes work in Dojo.

What _TemplatedMixin Provides

For the working developer, mixing _TemplatedMixin into a widget definition provides you with the following additional properties on your widget:

templateString        //    a string representing the HTML of the template

This property is deceptively simple — after all, how can so much power come from so little? The answer lies in what else _TemplatedMixin adds to your widget's definition.

A small note: templatePath is also added, but no longer used for template loading. It is still there for backwards-compatibility. We'll show you later on how to use dojo/text! to load a widget's template.

Overridden Methods

In addition to the property above, _TemplatedMixin overrides two methods defined in Dijit's widget architecture: buildRendering and destroyRendering. These two methods handle the parsing and filling out of the template (buildRendering) and destroying the widget's DOM correctly (destroyRendering).

Because both methods are critical to the templating process, if you override either of these methods in your custom code — make sure that you include a call to the parent version by adding this.inherited(arguments) in your overridden method. See the Understanding _WidgetBase Tutorial for more information on the widget lifecycle.

Using _TemplatedMixin

To make your custom widget "templatable", all you need to do is add dijit/_TemplatedMixin as the second or subsequent argument in the array of class declarations for your widget. For example, a SomeWidget widget might be declared like so:

define([
    "dojo/_base/declare",
    "dijit/_WidgetBase",
    "dijit/_TemplatedMixin",
    "dojo/text!./templates/SomeWidget.html"
], function(declare, _WidgetBase, _TemplatedMixin, template) {

    return declare([_WidgetBase, _TemplatedMixin], {
        templateString: template
    });

});

Dijit adheres to a standard of creating a separate directory called templates in the folder containing the widget module — a standard we'd advise you follow in your own code.

Notice that in our bare-bones declaration above, we used the templateString property in conjunction with a template loaded via dojo/text!{path}. This is the recommended way of setting up references to your template files, as it ensures that the files can be loaded asynchronously and properly integrated when creating a build of the Dojo Toolkit.

Now that we've set up our widget declaration to be template-based, let's write a template and talk about some of the special hooks in them that are available.

Writing Templates

A template is an HTML document fragment in which you define a DOM structure, along with any special "hooks" to tie things back into your widget declaration. Let's look at a quick example before diving into each of these hooks, and how variable substitution takes place in a template. Here's a hypothetical template for our SomeWidget, above:

<div class="${baseClass}">
    <div class="${baseClass}Title" data-dojo-attach-point="titleNode"
            data-dojo-attach-event="onclick:_onClick"></div>
</div>

While simple, this template demonstrates three of the most important aspects of the Dijit template system: variable substitution, attach points, and event attachments.

Note that when you define a template, it can only have one root node definition ( just like with XML documents). Multiple nodes at the top level is not allowed.

Variable Substitution

A template can have values set on DOM rendering though the use of a simple variable placeholder syntax, which looks like this:

${property}

The variable name is any property or field defined in your widget declaration; the example above used the property baseClass (available with any widget), but custom fields work just as fine — for instance, if we'd defined a property called foo in our SomeWidget, we would simply use ${foo} in our template. If the property in question happens to be a reference to an object, and you want to use the value of a property in that object, you may easily do so via normal object reference notation:

${propertyObject.property}

To prevent _TemplatedMixin from escaping quotations within a string, place a "!" before the full variable name, like so:

    ${!property}

Variable substitution in a template is only recommended for values that will not be changed during the lifetime of the widget. In other words, if you expect to be able to set the value of a property in a widget during the lifetime of your application programmatically, we recommend instead using your widget's postCreate method to set any variables programmatically through your widget's set() method.

Attach Points

Dijit's template system has a special attribute it will look for in your templates called an attach point — implemented using HTML5's data attribute syntax. An attach point tells the template renderer that when a DOM element is created with a data-dojo-attach-point attribute defined, to set the value of that attribute as a property of your widget to be a reference to the DOM element created. For example, the template for SomeWidget (above) defines two DOM elements. The main element (the outer div) can be referenced in your code through the property domNode, and the inner div element can be referenced in your code through the property titleNode.

Normally, the root node of your template becomes the domNode property of your widget, so you wouldn't normally include an attach point attribute in your definition. However, sometimes this is done in the template to allow the root node to also function with other subsystems, such as Dijit's focus manager.

The containerNode Attach Point

Dijit also defines a "magical" attach point called a containerNode. The basic concept of a container node is to provide some place for any additional markup to go if a widget is created declaratively. For example, given the template for SomeWidget:

<div class="${baseClass}">
    <div class="${baseClass}Title" data-dojo-attach-point="titleNode"
            data-dojo-attach-event="ondijitclick:_onClick"></div>
    <!-- And our container: -->
    <div class="${baseClass}Container"
            data-dojo-attach-point="containerNode"></div>
</div>

We might use it in declarative markup like so:

<div data-dojo-type="demo/SomeWidget"
        data-dojo-props="title: 'Our Some Widget'">
    <p>This is arbitrary content!</p>
    <p>More arbitrary content!</p>
</div>

When the Dojo parser traverses the document, it will find our example widget and instantiate it — and as part of that instantiation, any markup inside the widget will be appended to the containerNode. So when the widget is finished with its startup, the resulting DOM will look like this:

<div id="demo_SomeWidget_0" class="someWidgetBase">
    <div class="someWidgetTitle">Our Some Widget</div>
    <div class="someWidgetContainer">
        <p>This is arbitrary content!</p>
        <p>More arbitrary content!</p>
    </div>
</div>

Note that we removed some of the custom attributes for brevity; Dijit does not remove them when rendering templates.

Also be aware that if you embed other widget definitions in the main markup, and your widget has a containerNode attach point, any widgets will be instantiated inside the container node. For example, the following is a typical scenario when assembling an application:

<div data-dojo-type="demo/SomeWidget">
    <p>This is arbitrary content!</p>
    <div data-dojo-type="dijit/form/Button">My Button</div>
    <p>More arbitrary content!</p>
</div>

Event Attachments

In addition to attach points, the Dijit template system gives you a way of attaching native DOM events to methods in your custom widget. It does this through the use of the HTML5 data attribute data-dojo-attach-event. This is a comma-delimited string of key/value pairs (separated by colon); the key is the native DOM event to attach a handler to, and the value is the name of your widget's method to execute when that event is fired. If only a single event needs to be handled, omit a trailing comma. For example, here's the dojo-data-attach-event attribute defined on Dijit's MenuBarItem:

data-dojo-attach-event="onmouseenter:_onHover,onmouseleave:_onUnhover,ondijitclick:_onClick"

When your widget is instantiated and the DOM fragment is created from your template, the Dijit template system will then go through any attached event definitions and "auto-magically" wire these events (using dojo/on) from the resulting DOM and your widget object — making it incredibly simple to wire your visual representation to your controlling code. In addition, when those event handlers are fired, the same arguments normally passed by the native DOM event mechanism will be passed along to your widget's handler so that you have full access to what the browser is reporting.

Also, we want to use the dijit/_OnDijitClickMixin which adds in a modified event that supports more functionality than the standard DOM onclick event. Therefore we need to modify our widget declaration:

define([
    "dojo/_base/declare",
    "dijit/_WidgetBase",
    "dijit/_OnDijitClickMixin",
    "dijit/_TemplatedMixin",
    "dojo/text!./templates/SomeWidget.html"
], function(declare, _WidgetBase, _OnDijitClickMixin, _TemplatedMixin,
        template) {

    return declare([_WidgetBase, _OnDijitClickMixin, _TemplatedMixin], {
        templateString: template
        //    any custom code goes here
    });

});

We also need to modify our widget template:

<div class="${baseClass}">
    <div class="${baseClass}Title"
        data-dojo-attach-point="titleNode"
        data-dojo-attach-event="ondijitclick:_onClick"></div>
    <div>And our container:</div>
    <div class="${baseClass}Container"
        data-dojo-attach-point="containerNode"></div>
</div>

View Demo

The _WidgetsInTemplateMixin Mixin

Finally, Dijit's template system allows you to create more complex widgets from templates through the use of the _WidgetsInTemplateMixin mixin. This mixin tells the template system that your template has other widgets in it and to instantiate them when your widget is instantiated.

For example, let's modify both our declaration to always include a Dijit button:

define([
    "dojo/_base/declare",
    "dijit/_WidgetBase",
    "dijit/_OnDijitClickMixin",
    "dijit/_TemplatedMixin",
    "dijit/_WidgetsInTemplateMixin",
    "dijit/form/Button",
    "dojo/text!./templates/SomeWidget.html"
], function(declare, _WidgetBase, _OnDijitClickMixin, _TemplatedMixin,
            _WidgetsInTemplateMixin, Button, template) {

    return declare("example.SomeWidget", [_WidgetBase, _OnDijitClickMixin,
        _TemplatedMixin, _WidgetsInTemplateMixin
    ], {
        templateString: template
        //    your custom code goes here
    });

});

And then we would create a template like:

<div class="${baseClass}" data-dojo-attach-point="focusNode"
        data-dojo-attach-event="ondijitclick:_onClick"
        role="menuitem" tabIndex="-1">
    <div data-dojo-type="dijit/form/Button"
        data-dojo-attach-point="buttonWidget">
        My Button
    </div>
    <span data-dojo-attach-point="containerNode"></span>
</div>

Notice that in our modified template, we've added an attach point called buttonWidget along with the button's markup. This is an additional bonus of Dijit's attach point system; because the definition in question is to be a widget, the added property to our widget — myWidget.buttonWidget — will be a reference to the actual button widget, and not a reference to a DOM element. This allows you to create "uber-widgets" out of simpler building blocks, such as a widget to view a list of emails, a toolbar that has preset widgets in it, and a lot more.

Also, notice that you should require in any widgets into the module that are used by the template. You cannot take advantage of the "auto-require" feature of the dojo/parser introduced in Dojo 1.8 with widget templates because the creation life-cycle is synchronous but the auto-require feature has to run asynchronously.

Unless you have an explicit need to define widgets in your templates, don't mixin dijit/_WidgetsInTemplateMixin. The extra overhead that it incurs can affect the performance of the widget, and your application, if overused.

Conclusion

In this tutorial, we've learned about Dijit's powerful template system as implemented by the mixins _TemplatedMixin and _WidgetsInTemplateMixin, and how you can use this system to quickly create your own custom widgets for use in your applications. We've reviewed how the template system's attach points and event attachments allow you to quickly bind DOM elements to your code, and how to replace values in your template — as well as how to include other widgets in your widget template to create "uber-widgets".

Happy widget building!