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.

Tip

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.

Warning

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.

Warning

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

Warning

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.

Get Dojo: The Definitive Guide now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.