This tutorial is for Dojo 1.6 and may be out of date.
Up to date tutorials are available.
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.
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:
This is arbitrary content!
More arbitrary content!
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 ButtonMore 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 DemoThe 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 templateMy Button
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!