Ok, so it's been a little slow in updating this, mainly because I've been busy
using Dojo. I have continued working on the application that I showed a tiny bit of last time, which now has a name: LewenTrans (because it helps with transcription into IPA and calculation of Lewenstein distances).
Last time we saw the basics of using Dojo templates. It looked like a lot of work to just get a single variable into a template, but we only scratched the surface of what the Dojo Templated system can do.
Astute readers may have noticed that in the template, there was a div that looked like this:
<div dojoAttachPoint="tipaDiv"></div>
This special attribute performs a small but extremely useful bit of magic: It assigns that DOM node to a field of that name in your widget object. Why is this so useful? Couldn't I just have used id=tipaDiv and document.getElementById() like normal humans?
Here we get to one of the main benefits of using Dojo Templated: It handles namespaces for you. Normally, when you make a web page, you'd have to take care to not use the same ID several times, or the browser will complain. Once you get into Javascript, you'll have to be even more careful to not define the same Javascript function twice, as the browser will
not complain, merely call the last once whether you wanted it to or not. This is bad enough, but consider what happens once you start writing enough JavaScript that you want to reuse some of the code: Each bit of JavaScript you include might overwrite function or variables from other bits, and keeping track of this is a nightmare -- especially since you're not necessarily notified of this happening, things just go mysteriously wrong down the road. I can well imagine many people eschewing the use of Javascript just because of this "feature".
That hell of clashing namespaces is what Dojo Templates helps you with. Not just each class[1], but each instance of your class has its own namespace. Let's see how to do this. If I have a widget called swadesh.languagePicker that creates a little Ajax-based drop-down to pick a language, I can add it to the swadesh.tipaTable class by adding these two functions[2]:
postCreate: function() {
var lp = new swadesh.languagePicker(
{callback: dojo.hitch(this, "languageSelected")});
this.tipaDiv.appendChild(lp.domNode);
},
languageSelected: function(language) {
console.log("Got language " + language.toSource());
}
This not just creates a new languagePicker instance and plugs it into my HTML, it also gives that instance a callback function that can be called when the language has been selected. Let's take a look at the details:
The argument to languagePicker is an object, which on the languagePicker side is a set of keyword arguments. Customarily, the constructor uses dojo.mixin to add all the keys as fields on the instance. In this case, it's just the callback field, but it could be many others -- up to and including redefinitions of functions, if you feel foolhardy.
The
dojo.hitch
function makes a
thunk out of an object and the name of a function. In this way, the languagePicker does not have to think about the fact that it should call the callback on another object, hitch encapsulates that. Very sneaky.
Next we see the use of the
this.tipaDiv
field which was assigned due to the dojoAttachPoint in the template. This is the separate namespace in action -- every time I make a new object, the tipaDiv field is a new part of the DOM tree. I can create as many as these as my poor browser will carry on its back, and they will all have each their little tipaDiv.
The template only gets processed once the constructor has been run (and the constructor could indeed create it on the fly if it doesn't require Ajax calls for it), which is why we use the automagically called postCreate function for setting this up. This function gets called once the template has been processed and it's all set up in the web page, ready for our manipulation.
The argument to appendChild is not the languagePicker object, since that object is not a DOM object, but something we have "handcrafted". However, the object has a field domNode that contains the instance of the template that is associated with our object. That one can be inserted in the DOM.
Now let's consider what would happen if we were to create two tipaTable objects (which I will need to do soon). Had these things been done with ordinary functions and id attributes, we would have had to mangle the ids and function names to keep them distinct. With Dojo Templated, all that is handled for us implicitly[3].
The computer savvy amongst you may already be thinking "why can't I use the dojoType attribute to embed this widget automagically from the template?" It's simple recursion, after all. You can, but there are a couple caveats:
First, you will need to tell the widget that there can be other widgets defined in the template, otherwise they won't get processed. This is done by adding a field
widgetsInTemplate: true,
to the widget whose templates contains other widgets.
Second, you might want to use Ajax to get some data to feed the inner widget, I do that on several occasions. Not so here though, so why didn't I use dojoType?
Because, third, you cannot (AFAICT) specify a hitched function in an attribute to the inner widget. Since I want a function on my object to be called, I need to create it by hand. Ways to fix this would be appreciated.
Now you have learned how to make Dojo widgets with JavaScript and how to use widgets within widgets, and also a very simple callback system. With this alone, you can do a fair amount of web page with not a lot of coding, but there's still more to come before we are truly Web 2.0, things like making Ajax calls, handling events and modifying the DOM tree. I'll get back to that.
[1] While Javascript don't have classes in the Java sense, the widget definitions function very much as such, and I will be referring to them as such.
[2] And adding dojo.require("swadesh.languagePicker") in the header.
[3] Though I wouldn't mind a shortcut for dojoAttachPoint, that's a long word to use all the time.