Sep
21
2010

Introduction to Custom Dojo Widgets


Programmers are drawn to Dojo for its incredible suite of user interface widgets, better known as Dijits. If you are not familiar with the breadth of Dijits that are available the Theme Tester is a good place to have a look around. Any doubts that you have about whether Dojo provides a suitable suite of widgets for your web application should go away quickly after playing with the Theme Tester for a few minutes. Another invaluable resource is the Dijit Reference Guide on dojotoolkit.org that shows examples of all of the widgets in the Dijit library.

Custom Dojo Widgets

Often lost in the shuffle is that you can write your own custom widgets in addition to using the off-the-shelf Dijits. Enterprise Web Applications are full of common UI elements and functionality that are ripe to be encapsulated into a reusable widget. Lucky for you, Dojo makes this process easy. The resulting code is cohesive and modular, just the way we like it.

Let’s build a simple Dojo Widget using some best practices. Now, if this were an ordinary Enterprise-y blog, our example widget would be some kind of ‘business card’ that showed the details of a corporate employee. But let’s be honest, corporate employees are boring. We will build a business card widget for a much cooler class of people. Who are the coolest people in the world? That’s right, high school students!

Building a High School Student Widget

Our widget will show a picture of a high school student and their pertinent details. I don’t know many high school students, so acquiring photographs for use in this demonstration proved challenging. It was really weird, these kids acted downright frightened when I approached them in the mall and asked if they wanted to be in a JavaScript blog. Youth these days! To solve this problem, I spoke with a renowned social anthropologist who explained to me that High School students can be classified into exactly three groups: Emo, Punk, and Geek.

Our finished product will look something like this 1998-era version of your author:

(You guys would have guessed that I was an Emo, huh?)

Widget Pieces

A typical Dojo Widget is comprised of three files:

  • A JavaScript file that declares the widget and defines its logic.
  • An HTML fragment file that declares the widget’s semantic markup.
  • A CSS file that defines the widget’s styling information.

These files don’t have to be placed in any particular location, but we will use the same convention that Dojo uses for its widgets and set up a directory structure like this:

Our widget will be called widgets.HighSchooler, and its JavaScript file will be placed in a corresponding resource located at widgets/HighSchooler.js. (If you read my previous post about writing modular JavaScript with Dojo, this will look familiar). Our HTML fragment will live in a folder called templates and our CSS file will live in a folder called themes ,with a sub-directory called highschool. The images will also be placed in the themes directory.

The JavaScript File

Let’s take a look at a stubbed version of our widget’s JavaScript file.

dojo.provide("widgets.HighSchooler");

dojo.require("dijit._Widget");
dojo.require("dijit._Templated");

dojo.declare("widgets.HighSchooler", [ dijit._Widget, dijit._Templated ], {
    templateString : dojo.cache("widgets",
    		                    "templates/HighSchooler.html"),
	name : "",
	grade : "",
	hero : "",
	stereotype : ""
});

Declaring a widget is straight-forward. We use dojo.provide to declare our namespace, use dojo.require to load the necessary dependencies, and then use dojo.declare for the actual widget definition. The first argument to dojo.declare is our widget’s name. We are using the pattern of giving our widget the same name as our namespace. The second argument is the widget’s super types. Dojo Widgets typically define two super types, dijit._Widget and dijit._Templated. _Widget provides our widget with necessary life-cycle methods, which we will soon explore. _Templated provides support for instantiating our widget from an HTML template.

The rest of the JavaScript defines the properties and methods for our HighSchooler widget. templateString is a special property that indicates where to find the HTML template file. The rest of the properties represent the meaningful fields of our HighSchooler.

The CSS File

.highSchooler {
	border:1px solid #CCCCCC;
	height:165px;
	margin: 10px;
}

.highSchooler .profile {
	 list-style:none outside none;
	 font: 1.3em 'Lucida Grande',sans-serif;
}

.highSchooler .avatar {
	float: left;
	margin:5px 30px 5px 10px;
}

.highSchooler .profile .label {
	font-weight: bold;
	padding-right: 5px;
}

Nothing special here, just some styles that our widget will use. The interesting thing here is that all styling information about the widget is encapsulated into this file. If we were clever about our styling information it would be possible to implement a skinnable widget, as Dojo does with the Dijit library. (This is beyond the scope of this exercise though, our widget’s styling is rudimentary).

The HTML Template

This file contains the actual HTML markup of our widget. In this case, we define a div that contains an image element for the avatar and another div that contains an unordered list to display the details of our HighSchooler. Notice that the relevant CSS classes are set on the nodes of this HTML fragment.

<div class="highSchooler">
  <div class="avatar">
    <img></img>
  </div>
  <div>
    <ul class="profile">
	  <li><span class="label">Name</span><span></span></li>
	  <li><span class="label">Grade</span><span></span></li>
	  <li><span class="label">Hero</span><span></span></li>
	</ul>
  </div>
</div>

Using dojoAttachPoint

There are certain elements in the widget that we will want to manipulate with JavaScript. In this case the elements of interest are the image tag that shows the avatar and the span elements next to the Name, Grade, and Hero labels. We’ll use an extremely cool feature of Dojo called dojoattachpoint to enable these elements to be programmatically manipulated. We’ll add dojoattachpoint attributes to each of the elements that we would like to manipulate like so:

<div class="highSchooler">
  <div class="avatar">
    <img dojoattachpoint="imageNode"></img>
  </div>
  <div>
    <ul class="profile">
      <li><span class="label">Name</span>
	    <span dojoattachpoint="nameNode"></span>
	  </li>
	  <li><span class="label">Grade</span>
		<span dojoattachpoint="gradeNode"></span>
	  </li>
	  <li><span class="label">Hero</span>
		  <span dojoattachpoint="heroNode"></span>
	  </li>
	</ul>
  </div>
</div>

By adding dojoattachpoint, we have provided access to these DOM nodes in the widget’s JavaScript file. A property is dynamically added to the instance of the widget whose value is the DOM node associated with the element with the dojoattachpoint attribute. The name of this property is the value of the dojoattachpoint attribute. For example, if we want to set the innerHTML of the ‘Name’ span to correspond to our widget’s fullName property, we can do so in the widget’s JavaScript file like this:

this.nameNode.innerHTML = this.fullName;

The rest of our widget’s DOM manipulation code follows this same pattern. One thing to point out is that we only want this code to run after the widget’s DOM is ready. Dojo provides lifecycle methods that get called during a widget’s initialization. The lifecycle method where it is safe to make DOM manipulations is called postCreate. We’ll add a postCreate function to our widget that sets up each of the span elements and sets the image to the appropriate avatar based on the stereotype property:

	postCreate : function() {
		this.nameNode.innerHTML = this.fullName;
		this.gradeNode.innerHTML = this.grade;
		this.heroNode.innerHTML = this.hero;

		var keys = {
				emo : "emo.png",
				geek : "geek.png",
				punk: "punk.png"
		};
		var key = keys[this.stereotype] || "na.png";
		var imagePath = dojo.moduleUrl("widgets",
				"themes/highschool/images/" + key).toString();

		dojo.attr(this.imageNode, "src", imagePath);
	}

That’s it! Our widget’s JavaScript is done. Here it is in its entirety with some helpful comments.

// dojo.provide allows pages to use all of the
// types declared in this resource.
dojo.provide("widgets.HighSchooler");

// dojo.require the necessary dijit hierarchy
dojo.require("dijit._Widget");
dojo.require("dijit._Templated");

dojo.declare("widgets.HighSchooler", [ dijit._Widget,
		dijit._Templated ], {
	// Path to the template
	templateString : dojo.cache("widgets",
			"templates/HighSchooler.html"),

	// Widget properties
	fullName : "",
	grade : "",
	hero : "",
	stereotype : "",

	// Manipulate the widget's DOM, when ready
	postCreate : function() {
		// Using the attributes defined via dojoattachpoint
		this.nameNode.innerHTML = this.fullName;
		this.gradeNode.innerHTML = this.grade;
		this.heroNode.innerHTML = this.hero;

		// Setting the avatar to be the appropriate image
		var keys = {
			emo : "emo.png",
			geek : "geek.png",
			punk : "punk.png"
		};
		var key = keys[this.stereotype] || "na.png";
		var imagePath = dojo.moduleUrl("widgets",
				"themes/highschool/images/" + key).toString();

		// Set the 'src' attribute on our <img>
		dojo.attr(this.imageNode, "src", imagePath);
	}
});

Programmatic Widget Creation

OK, we’ve got everything defined. The question is, how do we get these bad-boys into a page? There are two ways to instantiate a Dojo Widget, programmatic and declarative. We’ll look at programmatic creation first. To create an instance of a widget, you simply ‘new’ it up. Optionally you can pass the ID of a node in your page where you would like the widget to appear. Alternatively, you can use the widget’s placeAt function if you need more finely grained control of where it shows up.

// Always dojo.require the resources that will be used
dojo.require("widgets.HighSchooler");

// Widget instances are almost always created inside a 'ready'
// block, as they represent DOM manipulation
dojo.ready(function() {
    // Replace the contents of the node with ID 'nodeId'
   // with our widget
    var kalEl = new widgets.HighSchooler({
        fullName:"Clark Kent",
        grade:"Senior",
        stereotype:"geek",
        hero:"Myself?"}, "nodeId");

    // Place our widget at the end of the node with ID 'nodeId'
    var kalEl = new widgets.HighSchooler({
        fullName:"Clark Kent",
        grade:"Senior",
        stereotype:"geek",
        hero:"Myself?"});
kalEl.placeAt("nodeId", "last");

});

The literal object that we pass in the constructor corresponds directly to the properties of our widget. Dojo takes care of initializing them for us. Slick!

Programmatic Widget Creation In Action!

Here’s a little demonstration of programmatic widget creation. I used Dojo to bind an event to the button that uses the value in the combo to programmatically create a new widget and add it to the end of the node below. When the button is clicked, this code is run. Try it out. Making Geeks is fun!

// Get the value from the combo
var typeOfTeen = dijit.byId('highSchoolerSelect').attr('value');
var newHighSchooler = new widgets.HighSchooler({
    fullName : "I was",
    grade : "created",
    stereotype : typeOfTeen,
    hero : "programatically"});

newHighSchooler.placeAt("teensGoHere", "last");

Declarative Widget Creation

Sometimes it is more convenient to specify a widget in markup. Using markup is also a fantastic way to quickly prototype your widget as you are developing it. Dojo provides the dojoType attribute to facilitate this. To declare a widget using markup, you simply specify the type-name of the widget using the dojoType attribute, and then specify any widget properties using attributes of their own. So if we wanted to declare a punk named Chris Jaun who’s hero is Bill Bradley, we’d do it like this.

<div dojotype="widgets.HighSchooler"
       fullName="Chris Jaun"
       grade="Senior"
       stereotype="punk"
       hero="Bill Bradley">
</div>

Which would get rendered like this.

(For all of you XHTML purists out there, dojoType is being deprecated in Dojo 1.6 in favor of HTML5′s data-* mechanism. Hooray for clean W3C validation scans!)

One thing to point out about declaring widget’s using markup is that in order to get Dojo to automatically parse the page for them, you have to set the parseOnLoad attribute to true in Dojo’s djConfig configuration variable.

Conclusion

Like most Dojo features, custom widgets are an incredibly powerful mechanism that would be impossible to fully cover in a single blog post. As such, expect many future Enterprise Dojo posts about this topic. I hope that this post piqued your attention and that you are now hungry to learn more. Check out this article in on dojotoolkit.org for a more detailed look at writing your own widgets.

As your web application requirements get more mature, separating and organizing your presentation layer is critical. Custom Dojo widgets enable us to create modular, reusable user interface components, something that every enterprise-caliber web application needs.

Related Posts

About the Author: Dan Lee

Dan Lee is a Software Developer and Dojo enthusiast. He is the creator and co-developer of the ReminderFox Firefox add-on and the addictive iOS game, Rhymo. Find him on Twitter here. Any opinions expressed on this blog are his own, and not of his employer.

14 Comments + Add Comment

  • Fun read, Dan. I wish I could have seen this before spending a few hours trying to figure out how to place my own first widget in a node, stubornly shirking resources like dojotoolkit’s reference guide.

    I loved to catch my teachers in high school making mistakes so I’m pleased to point out that the images for your cards don’t load in IE. You have to use the “uri” attribute of the result of dojo.moduleUrl(), i.e:

    var imagePath = dojo.moduleUrl(“widgets”, “themes/highschool/images/” + key).uri;

    will work in IE and Mozilla.

    I don’t know why, but that would be a good lesson in itself.

    • Great catch Dave!

      My mistake was to assume that dojo.moduleUrl returned a string. As you point out, it actually returns an object (of ‘type’ dojo._Url) with a property called ‘uri’ that contains the URL string.

      I dug into the Dojo source (which is easier to do than most believe) and found that dojo._Url overrides the toString function to do this: dojo._Url.prototype.toString = function(){ return this.uri; };

      So, an even cleaner solution than accessing .uri is to simply call toString() on the object that dojo.moduleUrl returns. I have updated my code accordingly. Again, thanks for calling this out. I learned something new from it, and that is what this blog is all about!

      • Cool. So who will explain why passing the object to the image’s src attribute works in Mozilla (and apparently IE7) but not IE8?

        • If I had to guess, I would bet that on most browsers ‘toString()’ is being called somewhere in the process of updating the src attribute, but on IE8, this call to toString() is not being executed. It’s a great question, and one that is beyond my JavaScript Kung-fu at this point in time. Anybody know? Pete Higgins? :)

  • Glad to hear that future Dojo versions will do away with “custom” attributes, and instead will live off of the data-* attribute. I know it all works fine either way, but it just feels safer / cleaner being done via “standards”. Not to mention native JS will (some day) offer a way to do ell.data.dojoFooBar — So that’ll be nice to have (eventually).

  • Great Article. I just started diving into dojo toolkit and lovin’ it.

    How would you go about testing this? Would you use DOH? Something else? I am just trying to get a sense of what preferences seasoned dojo developers use for testing.

  • Thanks for the link, I will check that out.

  • This has been very helpful Dan. I’ve followed it to build my own widget – but I can’t figure out how the widget knows where the CSS is! or Dojo for that matter. Have you mentioned it on this page somewhere but I’ve missed it?

  • Hi Dan. Great article, very useful!
    I have a question about Widgets, is it possible to use widgets inside the HTML template?

Leave a comment