Chapter 12. Dijit Anatomy and Lifecycle

Like object-oriented concepts from any other programming paradigm, Dojo widgets—dijits—follow particular patterns for lifecycle events such as creation and destruction, are composed according to a particular an anatomical style, and are described by a somewhat specialized vocabulary. This chapter provides a summary of these topics with an extended discussion on the fundamentals of custom dijit design.

Dijit Anatomy

Although you already know that dijit is short for "Dojo widget," it's helpful to elaborate just a bit before we proceed any further. To be precise, a dijit is any Dojo class that inherits from a foundational class from Dijit called _Widget. This class is part of the toolkit's foundational dijit module, so the fully qualified name of the class is dijit._Widget. There are several other foundational classes from Dijit that you'll learn about, but _Widget is the one that provides the primary ancestor for any dijit.

As you learned in Chapter 10, dojo.declare saves you from writing a lot of mundane boilerplate; dijits follow suit by tucking away a lot of complexity in classes like _Widget. As you're about to see, there are a number of method stubs that you can override to achieve custom behavior, as opposed to engineering your own boilerplate.

You may be wondering why the _Widget class is prefixed with a leading underscore. When used in relation to dijits, the leading underscore almost always means that it should not be instantiated on its own. Rather, it is usually used as a superclass for an inheritance relationship.

Let's start out our discussion on dijits with the familiar constructs that define a dijit on a physical level—the HTML, CSS, JavaScript, and other static resources that you've been developing with all along. Then, with both feet firmly planted, we'll dig deeper into the dijit lifecycle model by building upon your knowledge of dojo.declare and work through a number of increasingly complex code examples that involve the design of a custom dijit.

Web Development Review

As anyone who's ever touched a computer knows, HTML is the de facto standard for displaying information in a web browser. You can standardize headings, paragraph division, form fields, and generally provide just about any kind of markup that's useful for displaying textual information and images. Still, HTML alone isn't very pleasing to the eye: there's no nice formatting involved, and the content is static. The overall structure of a page is quite simple, and it doesn't change in response to user interaction. Given what we've come to expect over the years, the web would be intolerably boring with HTML alone.

Bring in a dash of CSS, however, and the scene changes significantly. Suddenly, the aesthetic nature of the page improves. Whereas HTML alone provides content with very little visual appeal, CSS adds value by improving a page's layout and typesetting. But style alone still results in a static page that leaves the inherent dynamism of human interaction longing for something with a little more life. You could create some nicely typeset pages with HTML and CSS, but that's about it.

JavaScript provided the dynamism that styled HTML so sorely lacked and gave rise to DHTML, which fueled the increasingly interactive online experience that blossomed into this modern era of rich Internet applications. JavaScript brings a web page to life and enables that sheer contentment we enjoy when a simple mouse click, selection in a combo box, or casual keystroke makes you wonder if the computer is reading your mind.

Although we've all come to know a well-designed, interactive web page when we see one, the experience itself can still be quite difficult to achieve; the JavaScript that controls the HTML and CSS can often be quite complex, and even the cleverest of implementations may not be maintainable or especially noteworthy. The central issue at stake is that the HTML, CSS, and JavaScript can be difficult to integrate into a single, cohesive framework. With little cohesion amongst them in ad-hoc designs, synergy is difficult to achieve, and massive amounts of energy is lost in implementing the not-so-interesting boilerplate. Unfortunately, this laborious process can quickly drain motivation and creativity from the parts of the application that really matter.

Dijits to the Rescue

Fortunately, dijits make matters much easier by providing the foundation upon which you can build a more complex design. They unite the HTML, CSS, and JavaScript into a unified medium, and although not perfect, dijits allow you to think in an object-oriented context much more quickly than you would without them: the end result is that you can quickly get to the heart of your own application before so much of the energy and creativity dries up. Whereas you previously had to provide your own means of uniting the HTML, CSS, and JavaScript, Dojo now does that tiresome work for you, and leaves you to get to the good stuff more quickly.

Just like standalone classes, dijits are self-contained inside of a single directory that corresponds to a namespace. In addition to a single JavaScript file, however, the directory also contains dependencies such as image file and stylesheets. The inherent familiarity of a directory structure provides an innate portability that makes it trivial to share, deploy, and upgrade dijits. Maintenance is also eased because there are no binary formats to unravel, and each component of a dijit can be checked into a version control system like Subversion as a separate file.

While all of the resources that compose a dijit could just be thrown into a single directory in the one-big-pair-of-clown-pants approach, Figure 12-1 displays a common convention for laying out a dijit on disk. Basically, the convention is to just compartmentalize the various facets into subdirectories to make things more manageable.

Anatomy of a dijit on disk
Figure 12-1. Anatomy of a dijit on disk

Dijits unite the HTML, CSS, and JavaScript that are so very central to any web development effort and provide you with a single, unified means of structuring the creativity required of your own application. In the end, you'll save time, effort, and likely obtain a more efficient design. Note that the layout for a minimal dijit that doesn't include a template or CSS is simply a directory with a JavaScript file.

The layout shown in Figure 12-1 shows the template contained in its own separate HTML file, and this setup is typical during the development cycle because it allows members of the development team to work on the template, CSS, and JavaScript files separately.

Fetching the template requires the JavaScript engine to issue a synchronous call back to the server; however, Dojo provides a wonderful way to optimize that synchronous call out the picture entirely: you can include the template as an inline string that's inside of the JavaScript file. Plenty of examples are coming up that illustrate how simple it is to make this happen.

Dijit Lifecycle Methods

Let's now turn our attention to the central dijit lifecycle methods that _Widget provides. As you're about to see, _Widget packs a lot of power with only minimal effort required on your part. By simply including it as the primary superclass ancestor in the inheritance hierarchy, your subclass has immediate access to the standard dijit lifecycle methods it provides, and you may override any of these method stubs to produce custom behavior during the construction and destruction of the dijit.

For example, _Widget provides stubs to override before a dijit appears on screen, immediately after a dijit becomes visible, and when a dijit is just about to be destroyed. Each of these choke points can be immensely valuable times to synchronize with the server-side model, explicitly destroy objects (so as to avoid well-known memory leaks), or do some tactical DOM manipulation. Regardless of the particulars for each and every situation, this is boilerplate that you don't have to write; it's already in place, and you can use it if and when you need it.

To introduce what _Widget offers, Example 12-1 shows a simple class that defines a class inheriting from _Widget and overriding the key methods involved in construction and destruction to produce debugging messages in the Firebug console. As you know from the last chapter, this file would be named Foo.js, and would be located in a directory named after the module—nothing more than a class mapped to a namespace.

The key point to observe in this example is that you override the inherited methods from _Widget just like you would expect. Take a look, and then we'll review each of these methods in more detail.

Example 12-1. Subclassing from _Widget
dojo.require("dijit._Widget");
dojo.addOnLoad(function(  ) {
    dojo.declare(
      "dtdg.Foo", // the subclass
      dijit._Widget, // the superclass
      {
        /* Common construction methods in chronological order */
        constructor : function(  ) {console.log("constructor");},
        postMixInProperties : function(  ) {console.log("postMixInProperties") ;},
        postCreate : function(  ) {console.log("postCreate");},

        /* Your clever logic goes here */
        talk : function(  ) {console.log("I'm alive!");},

        /* Canonical destructor, implicitly called via destoryRecursive(  ) */
        uninitialize : function(  ) {console.log("uninitialize");}
      }
    );
});
foo = new dtdg.Foo(  );
foo.talk(  );
foo.destroyRecursive(  ); /* Calls uninitialize, among other things */

When you run that example, you should notice the following output in the Firebug console:

constructor
postMixInProperties
postCreate
I'm alive!
uninitialize

The _Widget Lifecycle

To come full circle to the discussion about the creation pattern dojo.declare provides from back in Chapter 10, here's how the _Widget lifecycle plugs in:

preamble(/*Object*/ params, /*DOMNode*/node)
     //precursor to constructor; can manipulate superclass constructor args

constructor(/*Object*/ params, /*DOMNode*/node)
    // fire any superclass constructors
    // fire off any mixin constrctors
    // fire off the local class constructor, if provided

postscript(/*Object*/ params, /*DOMNode*/node)
    //_Widget implements postscript to kick off the create method...
    _Widget.create(/*Object*/params, /*DOMNode*/node)
    _Widget.postMixInProperties(  )
    _Widget.buildRendering(  )
    _Widget.postCreate(  )

The take away is two-fold:

  • _Widget builds right on top of what dojo.declare already provides and hooks into the postscript method in order to fire off the create method that systematically calls _Widget specific lifecycle methods.

  • A widget, as an ancestor of _Widget, is a bona fide JavaScript Function object. Sure, there's a lot of flare and pizzazz involved, but in the end, it comes right back to the basics.

Lifecycle methods

A flattened version of the lifecycle follows along with a short synopsis of what each _Widget lifecycle method accomplishes. It's flattened out and starts with preamble because it's quite uncommon to override postscript or the create method yourself (although you could if you wanted to devise your own widget lifecycle methods instead of using the standard ones). Expanded examples that more thoroughly cover each method appear later in this chapter.

preamble (originating from dojo.declare)

Preamble provides an opportunity to manipulate arguments before constructor receives them. If you override preamble, know that the same arguments that would normally be passed to constructor are passed to preamble and whatever preamble returns is what gets passed into constructor. This method is somewhat of an advanced feature and used infrequently compared to other lifecycle methods such as, for example, postCreate.

constructor (originating from dojo.declare )

This is the first method that you can override to perform custom behavior during dijit construction. There are two particularly common operations that are performed in constructor. One is including the initialization of dijit properties that are not primitive types. (Recall from Chapter 10 that declaring a complex type like an object or list inline as an object property causes it to be shared by all object instances.) Another common operation is adding any additional properties that are relied upon by other lifecycle methods downstream.

postMixInProperties (originating from dijit._Widget )

This method is called just after Dojo has walked the inheritance hierarchy and mixed all of the ancestors into the class. Thus, the name postMixInProperties literally refers to the time at which all a widget's properties have been mixed into the particular object instance. Therefore, by the time this method executes, your class has full access to those inherited properties and can manipulate them before the dijit visibly appears on the screen. As we'll soon see in an example that illustrates dijits that derive from a template, this method is typically the place where you'll modify or derive placeholders (indicated by ${someWidgetProperty} style notation) that appear in the template's markup.

buildRendering (originating from dijit._Widget )

In _Widget 's implementation, this method simply sets the internal _Widget.domNode property to an actual DOM element so that the dijit physically becomes a part of the page. Given that this method fires directly after postMixInProperties, it should now be all the more apparent why postMixInProperties is the canonical location for modifying a widget's template.

As you'll soon learn, another foundational Dijit class, _Templated, overrides this method to perform all of the myriad details that are involved in fetching and instantiating a dijit's template. Finally, note that just after buildRendering is called, the dijit itself is added to Dojo's dijit manager object so that the dijit can be properly destroyed during explicit destructor methods and/or when the page is unloaded. Some browsers do have well-known memory leaks that become relevant for long-running applications, and tracking widgets through a centralized registry is Dojo's way of helping to alleviate that problem. It is quite uncommon to override this method; you'll normally use the default implementation from _Widget or _Templated.

postCreate (originating from dijit._Widget )

This method executes once the dijit has been created and visibly placed in the page, so you can use it to perform any actions that might not otherwise be possible or prudent until that time. Take special care to perform actions that affect things such as a dijit's style or placement on the screen in postMixInProperties so that they occur before the dijit becomes visible. Performing those actions in postCreate may sometimes cause intermittent display "jerks" because you're manipulating the already visible dijit in this method; these issues can be difficult to locate and fix if you've forgotten the fundamental differences between postMixInProperties and postCreate. Additionally, note that if your dijit contains any child dijits, these children are not safely accessible here. To safely access child dijits, use the lifecycle method startup instead. To safely access other nonchild widgets, wait until the page has loaded via using dojo.addOnLoad.

startup (originating from dijit._Widget )

For child widgets declared in markup, this method automatically fires once the widget and all child widgets have been created. As such, this is the first safe place that a child widget could safely reference a child. As simple as it sounds, this task is often attempted in postCreate, which can lead to inconsistent behavior that can is difficult to detect and repair. For programmatically created widgets that contain other child widgets as part of a has-a relationship, you'll need to manually call startup yourself when you're sure that all child widgets have been created. The reason that you need to call it yourself for programmatically created widgets containing children is because it wouldn't make sense to proceed with sizing and rendering unless all child widgets have been added. (Otherwise, there could very well be lots of false starts.) This method is the final method stub that you can override for custom behavior to occur during dijit construction.

destroyRecursive (originating from dijit._Widget )

This method is the generic destructor to call in order to cleanly do away with a dijit and any of its child dijits. In the processing of destructing a dijit, this method calls uninitialize, which is the primary stub method that you can override to perform custom tear down operations. Do not override destroyRecursive. Provide custom tear-down operations in uninitialize and call this method (it does not get automatically called), which takes care of the rest for you.

uninitialize (originating from dijit._Widget )

Override this method to implement custom tear-down behavior when a dijit is destroyed. For example, you might initiate a callback to the server to save a session, or you might explicitly clean up DOM references. This is the canonical location that all dijits should use for these destruction operations.

Knowing the intricacies that distinguish the various lifecycle methods from one another is absolutely essential. Take special care to remember what type of behavior should be occurring in each method.

Especially common mistakes include:

  • Trying to manipulate a template after postMixInProperties has been called

  • Modifying a widget's initial appearance after postMixInProperties has been called

  • Trying to access child widgets in postMixInProperties instead of startup

  • Forgetting to perform any necessary destruction in uninitialize

  • Calling uninitialize instead of destroyRecursive

Essential properties

In addition to the _Widget methods just described, there are also some especially notable properties. Just like dijit methods, you can reference these properties with dot notation. You'll generally treat these properties as read-only:

id

This value provides a unique identifier that is assigned to the dijit. If none is provided, Dojo automatically assigns one. You should never manipulate this value, and in most circumstances, you won't want to use it at all.

lang

Dojo supports features for internationalization, and this value can be used to customize features such as the language used to display the dijit. By default, this value is defined to match the browser's setting, which usually matches that of the operating system.

srcNodeRef

If provided, this value populates the widget's domNode with the contents of the srcNodeRef and sets the domNode's id value to the id of the srcNodeRef.

domNode

This property provides a reference to the dijit's most top-level node. This property is the canonical node that is the visible representation of the dijit on the screen, and although you'll probably want to avoid direct manipulation of this property if using the _Templated mixin, it is helpful for some debugging scenarios. As previously mentioned, _Widget 's default implementation of buildRendering sets this property, and any methods that override buildRendering should assume this responsibility or else strange, mysterious things may happen. _Widget's default implementation of buildRendering sets domNode to either the value of srcNodeRef (if provided) or an empty DIV element.

Just in case you're wondering, here's a simple code snippet that you could run to inspect these properties in the Firebug console. Again, all of the properties are inherited from _Widget and are available via this, when this refers to the context of the associative array that is the third argument to dojo.declare:

dojo.require("dijit._Widget");
dojo.addOnLoad(function(  ) {
    dojo.declare(
      "dtdg.Foo",
      dijit._Widget,
      {
        talk(  ) : function(  ) {
          console.log("id:", this.id);
          console.log("lang:", this.lang);
          console.log("dir:", this.dir);
          console.log("domNode:", this.domNode);
        }
      }
    );
});
foo = new dtdg.Foo(  );
foo.talk(  );

Mixing in _Templated

While _Widget provides the foundational stub methods that you can override for creation and destruction events that occur during the lifecycle, _Templated is the previously alluded-to ancestor that actually provides the basis for defining a widget's template in markup and using substitutions and attach points to add functionality to it. Overall, it's a nice separation that lends itself to tooling and separates designer tasks from coding.

The vast majority of _Templated 's work involves parsing and substituting into a template file. An important part of this work entails overriding _Widget 's buildRendering method, which is where all of the template mangling takes place. Three very important concepts for templates include:

Substitution

Dijit uses the dojo.string module to perform substitutions into templates using the ${xxx} style dojo.string syntax. This is handy for taking widgets attributes that are passed in on construction and using them to customize templates.

Attach points

When the special dojoAttachPoint attribute is used in a template node, it provides the ability to directly reference the node via the attribute value. For example, if a node such as <span dojoAttachPoint="foo">...</span> appears in a template, you could directly reference the node as this.foo (in postCreate or later).

Event points

Similar to attach points, you can use the special dojoAttachEvent attribute to create a relationship between a DOM event for a node in a template and a widget method that should be called when in response to the DOM event. For example, if a node were defined, such as <span dojoAttachEvent="onclick:foo">...</span>, the widget's foo method would be called each time a click occurred on the node. You can define multiple events by separating them with commas.

Like _Widget, _Templated is given more thorough coverage with some isolated examples in just a moment. You're being exposed to it now so that you have a general idea of its overall purpose.

Lifecycle methods

The most notable effect of mixing in _Templated is that it results in overriding _Widget 's buildRendering method. Here's a synopsis of buildRendering :

buildRendering

While _Widget provides this method, _Templated overrides it to handle the messy details associated with fetching and instantiating a dijit's template file for on screen display. Generally speaking, you probably won't implement your own buildRendering method. If you ever do override this method, however, ensure that you fully understand _Templated 's implementation first.

Essential properties

Here are _Templated 's essential properties:

templatePath

Provides the relative path to the template file for the dijit, which is simply some HTML. Note that fetching the template for a dijit requires a synchronous network call, although Dojo will cache the template string after it is initially fetched. A discussion of producing a custom build of your dijits with tools from Util so that all template strings are interned is included in Chapter 16.

templateString

For dijits that have been designed or built to have their template strings interned inside of the JavaScript file, this value represents the template. If both templatePath and templateString are defined, templateString takes precedence.

widgetsInTemplate

If dijits are defined inside of the template (either path or string), this value should be explicitly set to true so that the Dojo parser will know to search for and instantiate those dijits. This value is false by default. Including dijits inside of other dijit templates can be quite useful. A common mechanism for passing values into child widgets that appear in a parent widget's template is via the ${someWidgetProperty} notation that is used for substitution.

containerNode

This value refers to the DOM element that maps to the dojoAttachPoint tag in the web page that contains your dijit. It also specifies the element where new children will be added to the dijit if your dijit is acting as a container for a list of child dijits. (Dijits that act as containers multiply inherit from the Dijit _Container class, and the dijits that are contained inherit from the Dijit class _Contained.)

Your First Dijit: HelloWorld

After all of that narrative, you're no doubt ready to see some code in action. This section provides a series of increasingly complex "HelloWorld" examples that demonstrate fundamental concepts involved in custom dijit design.

Let's build a canonical HelloWorld dijit and take a closer look at some of the issues we've discussed. Although this section focuses exclusively on what seems like such a simple dijit, you'll find that there are several intricacies that we'll highlight that are common to developing any dijit.

Figure 12-2 illustrates the basic layout of the HelloWorld dijit as it appears on disk. There are no tricks involved; this is a direct instantiation of the generic layout presented earlier.

Basic layout of a minimalist HelloWorld dijit
Figure 12-2. Basic layout of a minimalist HelloWorld dijit

HelloWorld Dijit (Take 1: Bare Bones)

The first take on the HelloWorld dijit provides the full body of each component. For brevity and clarity, subsequent iterations provide only relevant portions of components that are directly affected from changes. As far as on disk layout, these examples assume that the main HTML file that includes the widgets is located alongside a dtdg module directory that contains the widget code.

HTML page

First, let's take a look at the HTML page that will contain the dijit, shown in Example 12-2. Verbose commenting is inline and should be fairly self-explanatory.

Example 12-2. HelloWorld (Take 1)
<html>
  <head>
    <title>Hello World, Take 1</title>

<!--
   Because Dojo is being served from AOL's server, we have to provide a
   couple of extra configuration options in djConfig as the XDomain
   build (dojo.xd.js) gets loaded.

   Thus, we associate the "dtdg" namespace w/ a particular relative path
   on disk by specifying a baseUrl along with a collection of namespace mappings.
   If we were using a local copy of Dojo, we could simply stick the
   dtdg directory beside the dojo directory and it would have been
   found automatically.

   Specifying that dijits on the page should be parsed on page load
   is normally standard for any situation in which you have dojoType tags in the page.

-->
  <script
    type="text/javascript"
    src="http://o.aolcdn.com/dojo/1.1/dojo/dojo.xd.js"
    djConfig=isDebug:true,parseOnLoad:true,baseUrl:'./',modulePaths:{dtdg:'dtdg'}">
  </script>

<!--
    You'll normally include the dojo.css file, followed by
    any of your own specific style sheets. Remember that if you're not using
    AOL's XDomain build, you'll want to point to your own local dojo.css file.
-->
<link
    rel="stylesheet"
    type="text/css"
    href="http://o.aolcdn.com/dojo/1.1/dojo/resources/dojo.css">
</link>

<link
    rel="stylesheet"
    type="text/css"
    href="dtdg/themes/hello/hello.css">
</link>

<script type="text/javascript">

    dojo.require("dojo.parser");

    //Tell Dojo to fetch a dijit called HelloWorld that's associated
    //with the dtdg namespace so that we can use it in the body.
    //Dojo will use the values in djConfig.modulePaths to look up the location.
    dojo.require("dtdg.HelloWorld");
</script>
</head>
<body>
    <!--
    This is where the Dojo parser swaps in the dijit from the
    dojo.require statement based on our parseOnLoad:true option.
    Any styles applied to the dijit are provided by the style sheets imported.
    -->
    <div dojoType="dtdg.HelloWorld"></div>
</body>
</html>

What you just saw is almost the bare minimum that would appear in any page that contains a dijit. There is a token reference to any relevant style sheets that are spiffing up the dijits, the customary reference to Base that bootstraps Dojo, and then we explicitly dojo.require in the parser and HelloWorld dijit we're using in the body of the page. The only remotely tricky thing about any of these things is properly mapping the dtdg module to its path on disk in djConfig.modulePaths.

CSS

A widget's style consists of ordinary CSS and any static support that may be necessary, such as images. The neat thing, however, is that the actual style for the dijit is reflected in the dijit template—not in the DOM element where the dojoType tag is specified. This is particularly elegant because it essentially makes your dijits skinnable, or in Dojo parlance, you can define themes for your dijits and change these themes by swapping out stylesheets.

In our example dijit, the style for an individual DIV element is purely pedagogical but does illustrate how you could style your own dijits. Our HelloWorld theme consists of a single CSS file with nothing more than the following style in it:

div.hello_class {
    color: #009900;
}

Template

Just like the style, our HTML template for the HelloWorld is minimal. We're simply telling Dojo to take the DIV tag that was specified in our HTML page and swap it out with whatever our template supplies—in this case, our template just happens to supply another DIV element with some style and inner text that says "Hello World".

Our actual template file contains nothing more than the following line of HTML:

<div class="hello_class">Hello World</div>

JavaScript

Although it looks like there's an awful lot going on in the JavaScript, most of the substance is simply extra-verbose commenting. We're still dealing with the basic constructs that have already been reviewed, and you'll see that it's actually pretty simple. Go ahead and have a look, and then we'll recap on the other end. As you'll notice, the JavaScript file is just a standard module:

//Summary: An example HelloWorld dijit that illustrates Dojo's basic dijit
//design pattern

//The first line of any module file should have exactly one dojo.provide
//specifying the resource and any membership in parent modules. The name
//of the resource should be the same as the .js file.
dojo.provide("dtdg.HelloWorld");

//Always require resources before you try to use them. We're requiring these
//two resources because they're part of our dijit's inheritance hierarchy.
dojo.require("dijit._Widget");
dojo.require("dijit._Templated");

//The feature rich constructor that allows us to declare Dojo "classes".
dojo.declare(
    "dtdg.HelloWorld",

    //dijit._Widget is the prototypical ancestor that provides important method
    //stubs like the ones below.
    //dijit._Templated is then mixed in and overrides dijit._Widget's
    //buildRendering method, which constructs the UI for the dijit from
    //a template.
    [dijit._Widget, dijit._Templated],
    {
        //Path to the template of this dijit. dijit._Templated uses this to
        //snatch the template from the named file via a synchronous call.
        templatePath: dojo.moduleUrl("dtdg", "templates/HelloWorld.html")
    }
);

In the inheritance chain, _Widget provides the prototypical ancestor that our dijit inherits from to become a dijit. Because this first example is minimalist, we didn't need to override any of _Widget 's lifecycle methods, but examples that override these methods are coming up. The mixin ancestor, _Templated, provides functionality that pulls in the template by overriding _Widget.buildRendering. The actual template was located via the templatePath property. Although using templatePath instead of templateString incurred the overhead of a synchronous call back to the server, the template gets cached after it has been retrieved. Therefore, another synchronous call would not be necessary if another HelloWorld dijit came to exist in the same page.

The first time Dojo fetches a template file for a dijit, the overhead of a synchronous call back to the server is incurred. Afterward, the template gets cached.

Although this example entails your screen simply displaying a message to the screen, there's a lot more than a print statement behind the scenes that makes this happen. Moreover, the effort involved in HelloWorld is pretty much the minimal amount of effort that would ever be required of any dijit.

Let's solidify your understanding a bit more by filling in some of the method stubs to enhance the dijit. Only instead of taking the direct route, we'll take a few detours. After all, what better way to learn?

HelloWorld Dijit (Take 2: Modifying The Template)

Suppose you want your dijit to be a little less generic. Instead of displaying the same static message every time the page is loaded, a good first step might be to make the custom message that is displayed dynamic. One of the wonderful mechanisms that Dojo employs for keeping the logical concept of a dijit cohesive is that you can reference dijit properties that are defined in your JavaScript source file inside the template. Although referencing dijit properties from inside the template is only useful prior to _Templated 's buildRendering method executing, you'll find that initializing some portion of a dijit's display before it appears on the screen is a very common operation.

Referencing a dijit property from inside of the template file is simple. Consider the following revision to the HelloWorld template file:

<div class="hello_class">${greeting}
</div>

In short, you can refer to any property of the dijit that exists from inside of the template file and use it to manipulate the initial display, style, etc. However, there is a small but incredibly important catch: you have to do it at the right time. In particular, dijit properties that are referenced in templates are almost always most appropriately manipulated in the postMixInProperties method. Recall that postMixInProperties is called before buildRendering, which is the point at which your dijit gets inserted into the DOM and becomes visible.

Recall that the canonical location to manipulate template strings is within the dijit lifecycle method postMixInProperties, which is inherited from _Widget. Manipulating template strings after this point may produce undesirable intermittent display twitches.

Without further ado, Example 12-3 shows how the dijit's JavaScript file should appear if we want to manipulate the properties in the template to display a custom greeting.

Example 12-3. HelloWorld (Take 2: postMixInProperties)
//An example of properly manipulating a dijit property referenced
//in a template string via postMixInProperties
dojo.provide("dtdg.HelloWorld");

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

dojo.declare(
        "dtdg.HelloWorld",
        [dijit._Widget, dijit._Templated],
    {
        greeting : "",

        templatePath: dojo.moduleUrl(
            "dtdg",
            "templates/HelloWorld.html"
        ),

        postMixInProperties: function(  ) {
            //Proper manipulation of properties referenced in templates.
            this.greeting = "Hello World"; //supply as static greeting.
        }
    }
);

HelloWorld Dijit (Take 3: Interning the Template)

As alluded to earlier, you can save a synchronous call back to the server by specifying the template string directly inside of your JavaScript file. The next variation on the HelloWorld in Example 12-4 demonstrates just how easy this is to do manually, but keep in mind that the Dojo build scripts found in Util can automate this process for all of your dijits as part of a deployment routine.

Example 12-4. HelloWorld (Take 3: templateString)
dojo.provide("dtdg.HelloWorld");
dojo.require("dijit._Widget");
dojo.require("dijit._Templated");

dojo.declare(

    "dtdg.HelloWorld",
    [dijit._Widget, dijit._Templated],
    {
        greeting : "",

        //Provide the template string inline like so...
        templateString : "<div class='hello_class'>${greeting}</div>",

        postMixInProperties: function(  ) {
            console.log ("postMixInProperties");

            //We can still manipulate the template string like usual
            this.greeting = "Hello World";
        }
    }
);

In this example, templateString provides the template inline, so there's no need for a separate template file. This, in turn, saves a synchronous call to the server. If you can imagine lots of dijits with lots of template strings, it's pretty obvious that baking the template strings into the dijit's JavaScript files can significantly reduce the time it takes to load a page. For production situations, you won't want to do without the Util's build system (Chapter 16) to automate these kinds of performance optimizations for you.

HelloWord Dijit (Take 4: Passing in Parameters)

As yet another improvement to our HelloWorld dijit, let's learn how to pass in custom parameters to dijits through the template. Given the previous example, let's suppose that we want to supply the custom greeting that is to appear in our widget from its markup that appears alongside the dojoType tag. Easy; just pass it in like so:

<div dojoType="dtdg.HelloWorld" greeting="Hello World"
></div>

Passing in the parameter for a widget that is programmatically created is just as simple:

var hw = new dtdg.HelloWorld({greeting :  "Hello World"
}, theWidgetsDomNode);

Of course, you are not limited to passing in values that are reflected in the template. You can pass in other parameters that are used in other ways as well. Consider the following DIV element containing a reference to your HelloWorld dijit that specifies two extra key/value pairs:

<div foo="bar" baz="quux" dojoType="dtdg.HelloWorld"></div>

Wouldn't it be handy to be able to pass in custom data to dijits like that so that they can use it for initialization purposes—allowing application-level developers to not even have to so much as even peek at the source code and only hack on the template a bit? Well, ladies and gentlemen, you can, and the JavaScript file in Example 12-5 illustrates just how to do it.

Example 12-5. HelloWorld (Take 4: custom parameters)
dojo.provide("dtdg.HelloWorld");

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

dojo.declare(
        "dtdg.HelloWorld",
        [dijit._Widget, dijit._Templated],
    {
        templateString : "<div class='hello_class'>Hello World</div>",

        foo : "",

        //you can't set dijit properties that don't exist
        //baz : "",

        //tags specified in the element that supplies the dojoType tag
        //are passed into the constructor only if they're defined as
        //a dijit property a priori. Thus, the baz="quux" has no effect
        //in this example because the dijit has no property named baz
        constructor: function(  ) {

            console.log("constructor: foo=" , this.foo);
            console.log("constructor: baz=" , this.baz);
        }

    }
);

As you might have noticed, there's an emphasis on making the point that you can only pass in values for dijit properties that exist ; you cannot create new dijit properties by tossing in whatever you feel like into the element that contains the dojoType placeholder tag. If you run the previous code example and examine the Firebug console, you'll see the following console output:

constructor: foo=bar
constructor: baz=undefined

While passing in string values to dijits is useful, string values alone are of limited utility because life is usually just not that simple—but not to worry: Dojo allows you to pass in lists and associative arrays to dijits as well. All that is required is that you define dijit properties as the appropriate type in the JavaScript file, and Dojo takes care of the rest.

The following example illustrates how to pass lists and associative arrays into the dijit through the template.

Including the parameters in the element containing the dojoType tag is straightforward:

<div
   foo="[0,20,40]"
   bar="[60,80,100]"
   baz="{'a':'b', 'c':'d'}"
   dojoType="dtdg.HelloWorld"
></div>

And the JavaScript file is just as predictable:

dojo.provide("dtdg.HelloWorld");

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

dojo.declare(

        "dtdg.HelloWorld",

        [dijit._Widget, dijit._Templated],

    {
        templateString : "<div class='hello_class'>Hello World</div>",

        foo : [], //cast the value as an array
        bar : "", //cast the value as a String
        baz : {}, //cast the value as an object

        postMixInProperties: function(  ) {
            console.log("postMixInProperties: foo[1]=" , this.foo[1]);
            console.log("postMixInProperties: bar[1]=" , this.bar[1]);
            console.log("postMixInProperties: baz['a']=",  this.baz['a']);
        }

    }
);

Here's the output in the Firebug console:

postMixInProperties: foo[1]=20
postMixInProperties: bar[1]=6
postMixInProperties: baz['a']=b

Note that even though the value associated with the dijit's property bar appears to be a list in the page that includes the template, it is defined as a string value in the JavaScript file. Thus, Dojo treats it as a string, and it gets sliced as a string. In general, the parser tries to interpret values into the corresponding types by introspecting them via duck typing.

Take extra-special care not to incorrectly define parameter types in the JavaScript file or it may cost you some debugging time!

HelloWorld Dijit (Take 5: Associating Events with Dijits)

As yet another variation on our HelloWorld dijit, consider the utility in associating a DOM event such as a mouse click or mouse hover with the dijit. Dojo makes associating events with dijits easy. You simply specify key/value pairs of the form DOMEvent: dijitMethod inside of a dojoAttachEvent tag that appears as a part of your template. You may specify multiple key/value pairs or more than one kind of native DOM event by separating them with a comma.

Let's illustrate how to use dojoAttachEvent by applying a particular style that's defined as a class in a stylesheet whenever a mouseover event occurs and remove the style whenever a mouseout event occurs. Because DIV elements span the width of the frame, we'll modify it to be an inline SPAN, so that the mouse event is triggered only when the cursor is directly over the text. Let's apply the pointer style to the cursor.

The changes to the style are simple. We change the reference to an inline SPAN instead of a DIV and change the mouse cursor to a pointer:

span.hello_class {
    cursor: pointer;
    color: #009900;
}

The JavaScript file in Example 12-6 includes the updated template string, illustrating that the use of dojoAttachEvent is fairly straightforward as well.

Example 12-6. HelloWorld (Take 5: dojoAttachEvent)
dojo.provide("dtdg.HelloWorld");

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

dojo.declare(
        "dtdg.HelloWorld",
        [dijit._Widget, dijit._Templated],
    {
        templateString :
        "<span class='hello_class' dojoAttachEvent='onmouseover:onMouseOver,
onmouseout:
        onMouseOut'>Hello World</span>",

        onMouseOver : function(evt) {
            dojo.addClass(this.domNode, 'hello_class');
            console.log("applied hello_class...");
            console.log(evt);
        },

        onMouseOut : function(evt) {
            dojo.removeClass(this.domNode, 'hello_class');
            console.log("removed hello_class...");
            console.log(evt);
        }

    }
);

See how easy that was? When you trigger an onmouseover event over the text in the SPAN element, style is applied with the dojo.addClass function, which is defined in Base. Then, when you trigger an onmouseout event, the style is removed. Neat stuff!

Did you also notice that the event handling methods included an evt parameter that passes in highly relevant event information? As you might have guessed, internally, dojo.connect is at work standardizing the event object for you. Here's the Firebug output that appears when you run the code, which also illustrates the event information that gets passed into your dijit's event handlers:

applied hello_class...
mouseover  clientX=64, clientY=11
removed hello_class clientX=65, clientY=16
mouseover  clientX=65, clientY=16

Take care not to misspell the names of native DOM events, and ensure that native DOM event names stay in all lowercase. For example, using ONMOUSEOVER or onMouseOver won't work for the onmouseover DOM event, and unfortunately, Firebug can't give you any indication that anything is wrong. Because you can name your dijit event handling methods whatever you want (with whatever capitalization you want), this can sometimes be easy to forget.

To be perfectly clear, note that the previous example's mapping of onmouseover to onMouseOver and onmouseout to onMouseOut is purely a simple convention, although it does make good sense and results in highly readable code. Also, it is important to note that events such as onmouseover and onmouseout are DOM events, while onMouseOver and onMouseOut are methods associated with a particular dijit. The distinction may not immediately be clear because the naming reads the same, but it is an important concept that you'll need to internalize during your quest for Dijit mastery. The semantics between the two are similar and different in various respects.

Parent-Child Relationships with _Container and _Contained

After you've been rolling with _Widget and _Templated for a while, it won't be long before you find that it's convenient to have a widget that contains some children widgets. The "has-a relationship" pattern is quite common in programming and it is no different with Dojo. The _Container and _Contained mixins are designed to facilitate the referencing back and forth between parents and children that often needs to happen. Table 12-1 summarizes the API.

Table 12-1. _Container and _Contained mixins

Name

Comment

removeChild(/*Object*/ dijit)

Removes the child widget from the parent. (Silently fails if the widget is not a child or if the container does not have any children.)

addChild(/*Object*/ dijit,

/*Integer?*/ insertIndex)

Adds a child widget to the parent, optionally using the insertIndex to place it.

getParent( )

Allows a child to reference its parent. Returns a dijit instance.

getChildren( )

Allows a parent to conveniently enumerate each of its children dijits. Returns an Array of dijit instances.

getPreviousSibling( )

Allows a child widget to reference its previous sibling, i.e., the one "to the left." Returns a dijit instance.

getNextSibling( )

Allows a child widget to reference its next sibling, i.e., the one "to the right." Returns a dijit instance.

You'll see these mixins used extensively when you learn about the layout dijits. Next, we'll look at an example.

Rapidly Prototyping Widgets in Markup

Now that you have a feel for exactly how the widget lifecycle works and have seen plenty of examples, it's time to demonstrate a tool that you can use for quick, lightweight prototyping. Declaration is a Dijit resource that allows you to declare a widget in markup without resorting to a separate JavaScript file; this approach can be a tremendous boon during a development cycle when you need to rapidly capture or test out an idea.

Example 12-7 shows our very first HelloWorld widget using Declaration to create a widget in a completely self-contained page.

Example 12-7. HelloWorld (Take 6: Declaration)
<html>
    <head>
        <title>Hello World, Take 6</title>

        <script
            type="text/javascript"
            src="http://o.aolcdn.com/dojo/1.1/dojo/dojo.xd.js"
            djConfig="isDebug:true,parseOnLoad:true">
        </script>

        <link
            rel="stylesheet"
            type="text/css"
            href="http://o.aolcdn.com/dojo/1.1/dojo/resources/dojo.css">
        </link>

        <!-- define your CSS inline -->
        <style type="text/css">
            span.hello_class {
                color: #009900;
                cursor: pointer;
            }
        </style>

        <script type="text/javascript">
            dojo.require("dijit.Declaration");
            dojo.require("dojo.parser");
        </script>
    </head>
    <body>

        <!-- delcare the widget completely in markup -->
        <div
            dojoType="dijit.Declaration"
            widgetClass="dtdg.HelloWorld"
            defaults="{greeting:'Hello World'}">
                <span class="hello_class"
                dojoAttachEvent='onmouseover:onMouseOver, onmouseout:onMouseOut'>
                    ${greeting}
                </span>

                <script type="dojo/method" event="onMouseOver" args="evt">
                    dojo.addClass(this.domNode, 'hello_class');
                    console.log("applied hello_class...");
                    console.log(evt);
                </script>

                <script type="dojo/method" event="onMouseOut" args="evt">
                    dojo.removeClass(this.domNode, 'hello_class');
                    console.log("removed hello_class...");
                    console.log(evt);
                </script>
        </div>

        <!-- now include it into the page like usual -->
        <div dojoType="dtdg.HelloWorld"></div>
    </body>
</html>

Hopefully you made the immediate connection that Declaration is terrific for quickly working up an example with no hassle. There's no switching between and keeping track of multiple files, declaring module paths, and otherwise spending time on anything except the core task—so you can stay focused on the task at hand and get your work done as effectively as possible. Table 12-2 shows the Declaration API.

Table 12-2. Attributes of Declaration

Attribute

Comment

widgetClass

The widget's class

defaults

Attribute values that you'd normally pass in as parameters for construction

mixins

An Array that defines any mixin ancestors

The mixins attribute for a Declaration declared in markup must be an Array. This is different from dojo.declare, which allows for the possibility of either an Object ancestor or an Array of Object ancestors.

You'll generally want to refactor the work you do with Declaration after your idea settles, but there's really no faster way to mock-up a good idea in a hurry.

Summary

After reading this chapter, you should:

  • Be able to explain how dijits encapsulate the HTML, CSS, and JavaScript into a standalone, portable unit of code

  • Understand the key lifecycle events that _Widget provides with stub methods, including the order they execute and the stubs they provide

  • Understand how _Templated acts as a mixin ancestor for _Widget and provides supplemental functionality for adding template support to dijits

  • Understand the differences and trade-offs between using templatePath and templateString in templated dijits

  • Be able to successfully manipulate a dijit's template before it is displayed on screen

  • Be able to pass in parameters to dijits through their templates

  • Be able to programmatically create a widget and place it into the page

  • Know how to add support for DOM events such as onmouseover in your dijits

  • Be able to use Declaration to rapidly prototype in markup

A discussion of form widgets is next.