ONLamp.com
oreilly.comSafari Books Online.Conferences.

advertisement


The Mojo of Dojo

by Matthew Russell
11/01/2007

Editor's Note: This short article provides a great overview to the Dojo Toolkit, but be sure to check out our new Short Cut entitled Get Up And Running With Dojo that digs much deeper. We also have a new book coming out early next year, so stay tuned!

Dojo is an incredibly powerful toolkit that provides a wealth of web development utilities that you don't want to miss out on. In addition to coming packed with rich, turn-key Web 2.0 widgets for you to snap into your applications, it also includes the standard JavaScript library you've always wanted, facilities for compressing your code when it comes time for production, AJAX utilities, a suite of object oriented programming (OOP) constructs that reduces the boilerplate you'd otherwise have to write and maintain your own custom widgets, and more.

To say the least, Dojo is experiencing some serious momentum right now. Version 1.0 just landed, so get it while it's hot. Let's kick off our time together with a bird's eye view of Dojo's architecture and then follow up with some examples of how Dojo approaches OOP, AJAX, and widgets.

Dojo Architecture

The refactoring involved since the version 0.4 days introduced significant improvements to the codebase, so a high-level view of the toolkit is a great way to get rolling. Architecturally, Dojo consists of five primary components: Base, Core, Dijit, DojoX, and Util. Here's a short synopsis of each complement; afterward, we'll dig into some more specifics.

Base

Think of Base as the kernel of the toolkit. It's a tiny library wrapped up into a single JavaScript file (dojo.js) that provides the foundation for everything in the toolkit. Among other things, Base handles bootstrapping the toolkit, includes convenient language and AJAX utilities, provides mechanisms for simulating class-based inheritance, offers a slick packaging system that preserves the global namespace, and more.

That may sound like a lot (and it is), but the footprint of Base has been optimized to be tiny, so you won't see much overhead when you include it in your page. Base comes across the wire from AOL's Content Developer Network (CDN) at about 25 KB, and when you consider that simple Flash-based advertisements that inundate the Web are generally larger than that very size, Base seems all the more amazing.

By the way, cross-domain (XDomain) loading Dojo from AOL's server is an incredibly sweet deal and is especially noteworthy on its own. The CDN gives you full access to key parts of the toolkit without any setup or server maintenance on your own. You simply drop a reference like <script type="text/javascript" src="http://o.aolcdn.com/dojo/0.9.0/dojo/dojo.xd.js"></script> into your page, and have at it!

Core

Core builds on Base by providing additional facilities for accessing data stores, providing effects such as wipes/slides, internationalization (i18n), and back-button handling among other things. For the most part, any module or resource associated with the dojo namespace that you have to explicitly import into the page is part of Core. So, why bother with a separation between Base and Core? Basically, the division between Base and Core is a balancing act that's the result of providing fundamental support for common operations in Core (associated with the dojo namespace), while keeping Base as streamlined as possible.

The distinction between what did and didn't make it into Core may not always seem like a perfect boundary depending on how you look at it, but you're really nit-picking when you get to analyzing at that level of granularity. Besides, Dojo's packaging system makes it absolutely trivial to pull in additional modules and resources as needed with simple dojo.require statements, which is very similar to the concept of import statements in Java or #include statements in C++ code.

Dijit

Dijit is shorthand for "Dojo widget" and, depending on the context and capitalization, could refer to a single Dojo widget (a dijit) or to the entire component of the toolkit containing all of Dojo's widgets (Dijit). Dijit directly leverages the power of Base and Core to provide a powerful widget layer that's highly usable and simple in design.

Plugging a dijit into a page is usually as easy as specifying a special dojoType tag inside of an ordinary HTML element—a dream come true for layout designers and high level mashup developers. If you're an application developer, using dijits allows you to achieve incredibly rich functionality without having to dig into tedious implementation details. Even if you're more of a library writing type or custom widget developer, following Dijit's style ensures your widgets will be portable and easy to use essentials for any reusable software component.

DojoX

DojoX stands for "Dojo Experimental" and contains features that stand a chance of one day migrating into Core, Dijit, or even a new Dojo module that doesn't exist yet. Think of DojoX as an incubator or sandbox. It's a place where functionality can be offered in a way that doesn't incur the overhead and due diligence of testing, documentation, and support that would be required of Core or Dijit—yet it does provide a way for new ideas that may not be fully mature to be included in the toolkit. Note that everything in DojoX does come tagged with an explicit warning label, the dojox namespace, and is required to have a comprehensive README file. Right now, the work that happening with charts, graphs, and the new grid is pretty exciting.

Although DojoX may often not be the place where application developers who are counting on guaranteed stability or an easy to use resource should be looking, it simultaneously serves two incredibly important purposes: it provides a proving ground for new features to potentially move into Core and Dijit, and it ensures that there are high standards and stable APIs for resources that are already included in Core and Dijit. This approach is genius when you think about it, because it strikes a sensitive balance for critical issues central to any community-supported OSS project.

Util

Util is a collection of Dojo utilities and includes the Dojo Objective Harness (DOH)--a testing framework, scripts for scraping documentation out of the Dojo source, and tools for leveraging Mozilla's powerful JavaScript engine, Rhino. DOH is a relatively new addition to Dojo and the tests you can produce with it will save you some serious development time. After all, you do want to benefit from well-defined, systematic tests for your widgets, don't you?

Mozilla's Rhino is used to create compressed builds of Dojo for production use; you can also use it to compress your own widgets. Rhino's effectiveness is especially noteworthy in that it never mangles a public API. It is not uncommon to reduce your overall JavaScript file sizes by more than 50 percent with Rhino. The compression is a marvelous feature that comes gratis with any custom build of your Dojo widgets.

Here's a diagram that's helpul for summing up how these different pieces relate to one another:

Dojo Architecture
Figure 1. The general architectural design of Dojo

Object-Oriented Dojo

Dojo does a fantastic job of simulating classes though its dojo.declare function. In addition to dojo.declare allowing you to loosely define a class, it also manages a lot of messy details for inheritance relationships behind the scenes so that you don't have to clutter up your design with them. In the end, dojo.declare is one of the crown jewels of Base, because it minimizes the amount of brittle boilerplate you'd normally have to write if you were handling this effort all on your own; plus, it significantly increases the maintainability of your code by making the class declaration and inheritance relationships apparent to the reader.

Here's a quick example of dojo.declare at work:

//A Momma Tigress
dojo.declare("my.Tiger", null,
{
    _name: null,
    _species: null,
    
    constructor : function(name) {
        this._name = name;
        this._species = "tiger";
    },

    talk : function() {
        console.log("I'm a "+this._species);
    }

});

//And A Poppa Lion...
dojo.declare("my.Lion", null,
{
    _name: null,
    _species: null,
    
    constructor: function(name) {
        this._name = name;
        this._species = "lion";
    },

    talk : function() {
        console.log("I'm a "+this._species);
    }

});

//Have A Baby Liger
dojo.declare("my.Liger", [my.Tiger, my.Lion],
{
    _name: null,
    _species: null,
    
    constructor : function(name) {
        this._name = name;
        this._species = "liger";
    },

    // Override the talk method with a custom one.
    talk : function() {
        console.log("My name is "+this._name+", and I'm a "+this._species);
    }

});

//And her name is lucy. Create her just like any other JavaScript object
lucy = new my.Liger("Lucy");

//Lucy, would you like to introduce yourself?
lucy.talk();

The first parameter to dojo.declare is a string value that indicates the namespace (my) and name of the class (Tiger, Lion, and Liger), and the second parameter designates the superclass for the inheritance relationship. The my.Tiger and my.Lion classes don't directly descend from any other class, so there is an explicit null value in place, but the my.Liger class directly inherits from both a my.Tiger and a my.Lion via a limited form of multiple inheritance. Although this example is very simple, dojo.declare is pretty sophisticated; it handles the prototype chaining and other messy details involved in simulating class-based inheritance behind the scenes for you.

In each class, _name and _species denote properties, while constructor denotes one of Dojo's built-in methods that provides an explicit place to handle some aspects of custom initialization—providing a name in this case. The talk() method is a user-defined method that uses the Firebug console to log a message if the browser is Firefox and has it installed. Otherwise, it uses Firebug Lite (bundled with Dojo) to log the message. Finally, note that you create an object instance of your class just like you would with any other object by using the new operator with a constructor function.

While this example just begins to scrape the surface of dojo.declare, hopefully it conveys the general idea of how you can reduce a lot of boilerplate and produce readable, maintainable code with it.

AJAX Made Simple

Base provides a lot of really handy AJAX functions for you to use, and like dojo.declare, these functions save you from a lot of boilerplate and let you concentrate on more important facets of your project than how you should implement a mechanism for failing gracefully when an asynchronous request fails or how you should handle the various content types that may be returned to you. To illustrate Dojo putting JavaScript's XMLHttpRequest (XHR) object to work, the following code snippet shows a typical GET request that uses the data returned to perform a simple update on the page:

<body>
  <script type="text/javascript">
    dojo.xhrGet({
        url : "./foo.json", //Returns /*{'bar':'baz'}*/
        handleAs : "json-comment-filtered", //MIME type
        sync : false, //Explicit asynchronous request

        //Run this function if the request is successful
        load : function(response, ioArgs) {
            console.log("successful xhrGet");
            console.log(response);

            //Our handleAs value, tells Dojo to strip
            //comments and convert the data to an object.
            dojo.byId("foo").innerHTML= response.bar;
            return response;
        },

        //Run this function if the request is not successful
        error : function(response, ioArgs) {
            console.log("failed xhrGet"); 
            return response;
        }
    });
   </script>
  <div id="foo"></div>
</body>

Now, wasn't that easy? We indicated that we'd like to asynchronously GET some comment-filtered (to avoid JavaScript hijacking) JSON data from a particular URL and provided functions for handling the success and failure cases. You can further customize the dojo.xhrGet function by providing additional parameters for timing out requests, preventing the browser from caching GET requests, automatically yanking form data to send to the server, and more. Dojo also includes similar XHR methods for other RESTful operations like POST, PUT, and DELETE.

Creating Your Own Widgets

No overview of Dojo would be complete without including a synopsis of widgets—or dijits in Dojo lingo. The following dijit is among the ranks of the simplest one possible. Its substance consists of a single JavaScript file that includes an inline template string and no custom theme that would normally be provided via a CSS stylesheet. Although the method stubs contain no functionality, hopefully, you'll find the commenting instructive.

First, take a look at the widget itself:

// A Very Simple Widget

dojo.provide("my.FooWidget");

// Dynamically pull in these resources
dojo.require("dijit._Widget");
dojo.require("dijit._Templated");

dojo.declare(

    // Declare a FooWidget associated with the my namespace.
    "my.FooWidget", 

    // Any superclass(es) the widget descends from.
    // _Widget and _Templated from dijit are the usual 
    // suspects for most widgets.
    [dijit._Widget, dijit._Templated],

    {

        // This is the template for the widget as it will exist in the 
        // DOM. We could have specified it in a separate file and used
        // the templatePath property to find it instead.
        templateString : "<span>Foo Widget</span>",

    
        // Any custom properties you need can be defined like so...
        someCustomProperty : "",


        // The following four methods are called in this particular order
        // as part of the widget's initialization...

        constructor : function() {
            /*
            This stub provides a way for you to perform any other custom
            initialization on your widget, including the management 
            of inheritance relationships. Application developers
            might also handle any custom parameters that have been
            passed to a widget in here without having to dig into any
            source code.
            */
            console.log("constructor");
        },

        postMixInProperties : function() {
            /*
            This method is a stub provided by _Widget and is called 
            after all superclass properties, if any, have been mixed
            into the widget. In this example, _Templated is providing
            some properties that get mixed in, including the templateString
            property you see above. A very common operation to perform
            here is manipulation of the template.
            */
            console.log("postMixInProperties");
        },

        postCreate : function() {
            /*
            This stub is also provided by _Widget, and once it has been 
            called, the widget has been placed in the DOM and is
            available on screen. You might override it to perform any
            additional initialization actions that require the widget
            to first be visible on the screen.
            */
            console.log("postCreate");
        },

        startup : function() {
            /*
            Here in startup, you can be assured that all of the
            widget's children, if there are any, have been initialized
            and are available. It is not uncommon (at all) for a good
            design to consist of a sophisticated widget that is broken
            down into a number of simpler child widgets.
            */
            console.log("startup");
        },

        // Any custom  methods...
        fooMethod : function() {
        }

    });

As you now know, there's not much more to creating a widget than using dojo.declare to provide a class definition along with overriding some inherited methods and writing some methods of your own. While this example included a templateString property inside of the JavaScript file, each widget can trivially house its template string in its own separate HTML file during the development cycle--an especially great thing if you need to divide the labor and divvy some work out to designers or layout artists.

Styling your widgets is just as easy; you simply include a CSS reference in your top level page that displays all of the widgets with the necessary classes to style your widgets in a thematic sort of way. Speaking of which, here's the page that includes the previous my.FooWidget via a simple reference with the dojoType tag:

<html>
  <head>
      <title>Simple Widget Example</title>

      <!-- You could include any CSS here with some LINK tags -->

      <script type="text/javascript">
          // Parse the page for widgets once it loads. Explicitly
          // indicate that console.* methods are being used for
          //debugging, and point to custom modules that are local
          djConfig={
            parseOnLoad: true, 
            isDebug: true,
            baseUrl: "./",
            modulePaths: {my: "relative/path/to/local/dijits"}
          };
      </script>
      
      <!-- Bootstrap Dojo From AOL's CDN-->
      <script 
          type="text/javascript"
          src="http://o.aolcdn.com/dojo/0.9.0/dojo/dojo.xd.js">
      </script>

      <script type="text/javascript">
          // Explicitly require the parser for swapping in your widgets
          // on page load.
          dojo.require("dojo.parser");

          // Require the widget being used in the page.
          dojo.require("my.FooWidget");
      </script>
  </head>
  <body>
      <!-- 
      By the time the page loads, Dojo will have replaced this stub
      with a my.FooWidget. Any custom parameters could be passed in
      here for custom initialization by including inline tags in the
      HTML element.
      -->
      <span dojoType="my.FooWidget"></span>

  </body>
</html>

While the previous example was quite simple, it conveys the general approach to creating any Dojo widget. If you don't take anything else away from this example, just make sure and remember all of the boilerplate that you don't have to write and maintain anymore, and imagine the time you'll save by letting Dojo handle the messy details of resolving dependencies, simulating class-based inheritance, and encapsulating the various facets of a widget into a single collective unit. That's a lot of time you can invest in your application and concentrate on far more interesting things.

Out of The Box Dijits

While you can use Dojo to create your own highly-sophisticated widgets, Dojo offers a wide array of very useful dijits that "just work." As you observed in the previous example, the effort required in using a well-designed dijit is minimal: you simply bootstrap Dojo, use dojo.require statements to specify your page's dependencies, and then use dojoType tags in placeholder elements so that the parser can swap in your widgets on page load. It's just that easy.

The mothership over at dojotoolkit.org, is constantly being updated to include a wealth of Dijit examples, and Dojo releases contain some demo applications in the dijit/demos/directory. Out-of-the-box dijits are a dream come true for web designers, because you get access to snazzy UI controls without having to delve into the low level design and implementation details that used to be the norm.

The Learning Never Ends

Dojo is an incredibly sophisticated toolkit, and this short article could only cover so much. Hopefully, you've seen enough Dojo to be exicted about it, so head on over and download the lastest release and get your hands dirty. Our new Short Cut on this topic, Get Up And Running With Dojo, provides a much more in-depth primer to OOP with Dojo, so be sure to check it out when you're ready to work through some of the nitty-gritty.

Matthew Russell is a computer scientist from middle Tennessee; and serves Digital Reasoning Systems as the Director of Advanced Technology. Hacking and writing are two activities essential to his renaissance man regimen.


Return to ONLamp.



Sponsored by: