- The Book of Dojo
- Quick Installation
- Hello World
- Debugging Tutorial
- Introduction
- Part 1: Life With Dojo
- Part 2: Dijit
- Part 3: JavaScript With Dojo and Dijit
- Part 4: Testing, Tuning and Debugging
- Part 5: DojoX
- The Dojo Book, 0.4
The Template
Submitted by dante on Tue, 08/14/2007 - 03:07.
Often, you'll find a widget that does almost exactly what you want ... but needs just a bit of help. The last thing you'd want to do is hack the source code. Good thing you don't have to! The same widget construction techniques apply to both creating and extending widgets. So first, let's extending an existing stable widget: the AccordionContainer and AccordionPane. Right now the pane titles can only be text, but suppose you want images there as well.
Most Dijit components revolve around a template. A template looks like a macro, and can perform simple substitutions of ${...} variables and substitutions of DOM nodes.
To see how the template language works, let's look at a plain ol' Accordion Container instance:
<div dojoType="dijit.layout.AccordionContainer">
<div dojoType="dijit.layout.AccordionPane" title="pane 1">
Text of Pane 1
</div>
<div dojoType="dijit.layout.AccordionPane" title="pane 2">
Text of Pane 2
</div>
</div>
<div dojoType="dijit.layout.AccordionPane" title="pane 1">
Text of Pane 1
</div>
<div dojoType="dijit.layout.AccordionPane" title="pane 2">
Text of Pane 2
</div>
</div>
The DOM that ends of in memory and on the screen is quite different. Though [View Source] in your browser shows the HTML above, Firebug shows the actual DOM used by the AccordionPane:

Dojo replaces the simple nodes of our example with groups of HTML elements. That's one of the jobs of the Dojo parser. The parser consults the widget's template to construct this group. Where's the template kept? For AccordionPane, you can find it in the source version of Dojo at dijit/layout/templates/AccordionPane.html:
<div class='dijitAccordionPane'
><div dojoAttachPoint='titleNode,focusNode' dojoAttachEvent='ondijitclick:_onTitleClick,onkeypress:_onKeyPress'
class='dijitAccordionTitle' wairole="tab"
><div class='dijitAccordionArrow'></div
><div class='arrowTextUp' waiRole="presentation">▲</div
><div class='arrowTextDown' waiRole="presentation">▼</div
><span dojoAttachPoint='titleTextNode'>${title}</span></div
><div><div dojoAttachPoint='containerNode' style='overflow: hidden; height: 1px; display: none'
dojoAttachEvent='onkeypress:_onKeyPress'
class='dijitAccordionBody' waiRole="tabpanel"
></div></div>
</div>
><div dojoAttachPoint='titleNode,focusNode' dojoAttachEvent='ondijitclick:_onTitleClick,onkeypress:_onKeyPress'
class='dijitAccordionTitle' wairole="tab"
><div class='dijitAccordionArrow'></div
><div class='arrowTextUp' waiRole="presentation">▲</div
><div class='arrowTextDown' waiRole="presentation">▼</div
><span dojoAttachPoint='titleTextNode'>${title}</span></div
><div><div dojoAttachPoint='containerNode' style='overflow: hidden; height: 1px; display: none'
dojoAttachEvent='onkeypress:_onKeyPress'
class='dijitAccordionBody' waiRole="tabpanel"
></div></div>
</div>
That's a lot of HTML to replace the one DIV tag. Mostly this looks like garden variety HTML with lots of CSS class markers to fill in the styling. That makes the theme system work well. But a few attributes and constructs are alien to HTML - these are Dojo's template language elements. In particular:
- ${title}
- is replaced with the title="..." attribute sent to the widget
- dojoAttachPoint='containerNode'
- An attachPoint is a tag merged with user-provided HTML. It's like a substitution variable for HTML.
- dojoAttachEvent='onkeypress:_onKeyPress'
- Connects an event to an event handler at that node.
Attach Points
The AttachPoint is a little bit complex, so let's look at that a bit more closely. Each widget has a special variable named containerNode which represents the unchanged HTML. The act of attaching involves three steps.
- Start with containerNode:
- mix in any attributes in the same tag as the attachPoint section (except the attachPoint attribute itself):
<div dojoType="dijit.layout.AccordionPane"
title="pane 1" style='overflow: hidden; height: 1px; display: none'
class='dijitAccordionBody' waiRole="tabpanel">
Text of Pane 1
</div> - Pop the result into the template
This is a recursive procedure so the containerNode itself can contain widgets.
A Template for ImageAccordion
ImageAccordion only needs a few small changes to the AccordionPane template. All of the rest of the code for AccordionPane can be reused. So here is our new template:
<div class='dijitAccordionPane'
><div dojoAttachPoint='titleNode,focusNode'
dojoAttachEvent='ondijitclick:_onTitleClick,onkeypress:_onKeyPress'
class='dojocAccordionTitle' wairole="tab"
><div class='dijitAccordionArrow'></div
><div class='arrowTextUp' waiRole="presentation">▲</div
><div class='arrowTextDown' waiRole="presentation">▼</div
><span dojoAttachPoint='titleTextNode'><img alt="${title}" src="${src}"
></span></div
><div><div dojoAttachPoint='containerNode'
style='overflow: hidden; height: 1px; display: none'
dojoAttachEvent='onkeypress:_onKeyPress'
class='dojocImageAccordionBody' waiRole="tabpanel"
></div></div>
</div>
><div dojoAttachPoint='titleNode,focusNode'
dojoAttachEvent='ondijitclick:_onTitleClick,onkeypress:_onKeyPress'
class='dojocAccordionTitle' wairole="tab"
><div class='dijitAccordionArrow'></div
><div class='arrowTextUp' waiRole="presentation">▲</div
><div class='arrowTextDown' waiRole="presentation">▼</div
><span dojoAttachPoint='titleTextNode'><img alt="${title}" src="${src}"
></span></div
><div><div dojoAttachPoint='containerNode'
style='overflow: hidden; height: 1px; display: none'
dojoAttachEvent='onkeypress:_onKeyPress'
class='dojocImageAccordionBody' waiRole="tabpanel"
></div></div>
</div>
What's with the > signs on different lines? That ensures extra whitespace is not included in the actual generated HTML. It makes the template slightly less readable, but pays big dividends in performance.
There are only a few changes from the original AccordionPane:
- The most important change is the replacement of ${text} with
. This means the ImageAccordion must be sent an additional attribute - src - with the URL for the image. - The titleNode now has the CSS class "dojocAccordionTitle"
- The containerNode now has the CSS class "dojocImageAccordionBody"
So now we have the heart of our new widget. Let's pop the template in and make things happen.
Widgets inside the Template
So what if we want the widget to have a widget inside of it, as in ...:
<div class="combinedDateTime">
<div dojoType="dijit.form.DateTextBox"></div>
<div dojoType="dijit.form.TimeTextBox"></div>
</div>
<div dojoType="dijit.form.DateTextBox"></div>
<div dojoType="dijit.form.TimeTextBox"></div>
</div>
When using this template in a directly extended
widget class, you will need to set the property widgetsInTemplate: true
. Why? Because a widget inside a template requires some recursive parsing, which may be slow if you're drawing thousands of widgets ... especially if there is nothing extra to parse. Therefore, it is false by default.
dijit.Declaration-based widget classes automatically set widgetsInTemplate to true.
- Printer-friendly version
- Login or register to post comments
- Unsubscribe post
nice to mention that templates are parsed differently?
over at the Dijit "common features" page I mentioned that script tags for creating methods seem to be handled differently in templates - for extension points, for example. if I am right that templates are parsed a little differently it might be nice to mention that fact in here,
(well, they are at least slightly differnt - for a start you have to set the widget member widgetsInTemplate to true in order to get parsing of cub widgets in a compound widget..)
Good Point
... so I put an extra section on it and briefly mentioned it in the next two pages as well. The only other difference I've been able to find is dijit.Declaration allows only one preamble.
This comment and reply will be removed on or after 12/31/07 to avoid confusion.
widgetsInClass: true ??
it should be widgetsInTemplate
See above
Can we get this page edited so that the comment above by Michael is refelected.
It's NOT: widgetsInClass, it's widgetsInTemplate that needs to be set to True.
sorry - done.
sorry - done.
Clarification: What dojoAttachPoint really is
(This section has some imperfections I'd like to try to correct: (1) has a section on how to add an attach point that missed the step of actually adding it, and (2) doesn't actually explain what an attach point is (I think).
This is my attempt to remedy these problems. Caveat lector.)
In the javascript of a widget, you often might wish to refer to some of its html template's dom nodes directly. For example in the Accordion widget, the js might want to access the nodes for the title, the title's text, the container or accordion pane, and so on.
You might think the widget author could just use ids in the html template, and then dojo.byId() in the widget's js. But if she does, then if two or more widget instances are created, they'll all have the same ids! Obviously code will blow up then.
Instead, you the widget author do the following:
1. In your widget's js, you use (without declaring them) variables for these nodes. For example, the Accordion widget uses variables named: titleNode, titleTextNode, and so on.
2. In your widget's template's html, for every node that these variables are supposed to correspond to (eg point to), you add the attribute: dojoAttachPoint="yourVariableNameHere".
So if you look at the first code sample above with dojoAttachPoint, the values of those attributes are all js variables (undeclared) that are used in the Accordion widget's js code.
The reason the variables are undeclared is that when the parser scans the html in step 1, and it finds the variables in the dojoAttachPoint attribute, *the parser* declares the variables.
Hope this helps,
/r:b:
Clarification: dojoAttachEvent
I got a clearer explanation of dojoAttachEvent from tk on irc that I'd like to share.
Problem: You've written a widget, and when the user clicks on it, something happens. What you want is for the programmer using the widget to be able to either *change* what happens, or have something happen in addition, without having to edit your widget.
Solution: To see how to do this, let's see how dijit.form.Button does it for clicking. Note that we need to distinguish between DOM events, which happen on DOM elements; and widget events, which fire when things happen in the widget. (To make this clearer: DOM onblur might fire on elements in your widget, but you would only want the widget's onBlur to fire when the mouse left the boundary of the widget.)
1. In your template html, on the html elements you want to have fire DOM events, add the attribute dojoAttachEvent as follows. Here's some of the dijit Button's Button.html (with ... where I've left stuff out):
also, 3c) if you want to add
also, 3c) if you want to add code to the plain old method call this.inherited(arguments) from the dojo/method:
3d) running code after the widget has been created
If you specify dojoType="dojo/method" without an event attribute, then the code inside that tag will be executed after the widget is created. Specifically, in the widget creation life cycle, it will run after _Widget.js::dijit._Widget->create (which calls, in that order, postMixInProperties, buildRendering, and postCreate).
To sum up:
<script type="dojo/method" event="onClick" args="evt">
alert("This will execute instead of the Button dijit's onClick method.");
</script>
<script type="dojo/connect" event="onClick" args="evt">
alert("This will execute after of the Button dijit's onClick method has been called.");
</script>
<script type="dojo/method">
alert("This will execute immediately after the Button dijit has been created.");
</script>
</div>
missing code for Step 1
(For some reason I can't edit my own post, there is no Edit link. In Step 1 above, the sample code in Button.html is showing up as just ... . Here's Step 1 rewritten with the missing code.)
1. In your template html, on the html elements you want to have fire DOM events, add the attribute dojoAttachEvent with -- well, let's just look at dijit.form.Button's Button.html template file to see what they added.
<div class="dijit dijitLeft dijitInline dijitButton"
dojoAttachEvent="onclick:_onButtonClick,onmouseenter:_onMouse,onmouseleave:_onMouse,onmousedown:_onMouse"
> ...
Note the part in bold.
The onclick refers to the DOM event on this div. The _onButtonClick refers to a plain old js method in Button.js. By plain old js method, I mean it's not a DOM event handler. It's plain old js, written by some dijit programmer.
So in your own template, connect DOM events to plain old js methods, in your widget's .js file, in the same way.
/r:b:
Sometimes you don't need dojoAttachEvent
The widget you are using may have already attached events to its plain old methods, in which case you won't have to re-attach them with dojoAttachEvent -- in other words, you can skip Step 1. Look inside the widget's source code to see.
Very confusing
The example shown on this page is very misleading. Firstly, the Attach points section, step 3 states to pop the result back into the template - but if you look at the final displayed HTML it does not have any dojoType attribute (which makes sense) yet the mix-in code in step 2 still does. So clearly we have not just 'popped the result back in' some other steps have been omitted - what are they?
rbondi's post above explains well how the attachpoint variables in the template bind to the js file variables for the widget. One thing to add here, containerNode is special it seems in that it binds the root tag (
<div>
in this case which contains the dojotype attribute declaring the use of the dijit) to this DOM node variable in the js file too. So if I understand correctly the attach points are really just declared mappings from HTML elements in both the web page which uses the widget AND the template to DOM node variables in the js file so that the widget can use these to manipulate *all* HTML as you would expect to produce the final version for the browser.Maybe this could be verified and clarified on this page for others? Thanks!