Creating Template-based Widgets

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

  • Difficulty: Intermediate
  • Dojo Version: 1.6

Getting Started

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

The basic concept of _Templated 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 _Templated defines (and why), and then build a simple widget from scratch using its functionality.

Note that _Templated 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).

What _Templated Provides

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

templateString,			//	a string representing the HTML of the template
templatePath,			//	a URL pointing at a file to be used as a template
widgetsInTemplate		//	a Boolean indicating whether or not child widgets
						//	are defined in the template

A small note: templatePath is normally not used any more, but is there for backwards-compatibility. We'll show you later on how to use dojo.cache with templateString instead.

These three properties are deceptively simple—after all, how can so much power come from so little? The answer lies in what else _Templated adds to your widget definition.

Overridden Methods

In addition to the properties above, _Templated overrides three methods defined in Dijit's widget architecture: buildRendering, destroyRendering, and startup. These three methods handle the parsing and filling out of the template (buildRendering), destroying the widget's DOM correctly (destroyRendering), and ensuring that any child widgets in a template are started correctly.

Because all three methods are critical to the templating process, if you override any 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.

Using _Templated

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

dojo.declare("example.SomeWidget", [ dijit._Widget, dijit._Templated ], {
	templateString: dojo.cache("example", "templates/SomeWidget.html")
	//	your custom code goes here
});

Dijit adheres to a standard of creating a separate directory called "templates" at the same level as the JavaScript declaration—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 dojo.cache, which is a function that will retrieve the requested resource either from memory or from the given URL (if not present in its internal cache). This is the recommended way of setting up references to your template files, as it ensures that as few HTTP connections are made as possible, and also has special treatment 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:


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, place a "!" before the full variable name, like so:

${!propertyObject.property}

As of Dijit 1.5, using 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 focusNode, and the inner span element can be referenced in your code through the property containerNode.

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 Dijit 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 (above), we might use it in markup like so:

This is arbitrary content!

More arbitrary content!

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:


Note that we removed some of the custom HTML5 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:

This is arbitrary content!

My Button

More arbitrary content!

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"

The event we defined in our example template, ondijitclick, is a modified handler set up by Dijit itself to support extra things that a normal onclick event does not capture; in general, you can use it in any place you'd normally use 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 automagically wire these events (using connect) 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.

View Demo

The widgetsInTemplate Property

Finally, Dijit's template system allows you to create more complex widgets from templates through the use of the widgetsInTemplate property. This property (by default, set to false) 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 and our template to always include a Dijit button:

//	our modified declaration
dojo.declare("example.SomeWidget", [ dijit._Widget, dijit._Templated ], {
	templateString: dojo.cache("example", "templates/SomeWidget.html"),
	widgetsInTemplate: true
	//	your custom code goes here
});

//	 our template

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.

Unless you have an explicit need to define widgets in your templates, leave the widgetsInTemplate property to be false, or simply don't include it in your widget definition (it is false by default). 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 mixin _Templated, 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!

Error in the tutorial? Can’t find what you are looking for? Let us know!