Dojo Custom Widget Tutorial

March 28, 2007 – 3:27 pm by coachwei | Category WebDev | 2 Comments »

In my effort to evaluate Dojo performance, I am going to build a custom Dojo widget and create my own build profile so that only the absolutely required files for my application are packaged into the initial download. However, considering the real world best practice, I would create my own directory structure and my own JavaScript package – all separate from Dojo code so that I can easily maintain my own code without being intermixed with Dojo.

The entire process looks like a good end to end tutorial on how to build custom Dojo widgets with some important real world considerations. So I thought of writing it up and hope it is helpful for folks who are interested in using Dojo. In the next post, I’ll go into some analysis of Dojo performance.

The Dojo custom widget I am building is very simple – it is largely based on the “Memo widget” example provided by Dojo Manual (I recommend you read the "Memo widget" tutorial at http://dojotoolkit.org/node/71 ). The Dojo version I used is 0.4.

Directory Structure:
For the reason of code maintenance, I am implementing this widget in my own directory structure and my own package so that it is physically separated from Dojo. The package name is “coach.widget”. The directory structure is shown below:

dojo widget directory structure


Widget Source Code:

Here is the source code of widget implementation, memo.js:

dojo.provide("coach.widget.Memo");
dojo.require("dojo.widget.Parse");
dojo.require("dojo.widget.HtmlWidget");

dojo.widget.defineWidget(
    // widget name and class
    " coach.widget.Memo", 

    // superclass
    dojo.widget.HtmlWidget,

    // properties and methods
    {
        // parameters
        title: "Coach Widget Note",

        // settings
        isContainer: true,
        templatePath: dojo.uri.dojoUri("../coach/widget/template/memo-template.html"),
        templateCssPath: dojo.uri.dojoUri("../coach/widget/template/memo-style.css"),

        // callbacks
        onClick: function(evt){
            this.destroy();
        }
    }
);

Note that this widget has its own template, memo-template.html, and its own style sheet, memo-style.css. The locations of these two files are specified in the “templatePath ” and “templateCssPath ” variables. Using “dojo.uri.dojoUrl ” tells Dojo loader that the URI value is relative to where dojo.js is located. In this example, they are located in the “template” directory that is a sibling of the “memo.js” file.

Here is memo-style.css:

.memo {
    background: yellow;
    font-family: cursive;
    width: 10em;
}

.title {
    font-weight: bold;
    text-decoration: underline;
    float: left;
}

.close {
    float: right;
    background: black;
    color: yellow;
    font-size: x-small;
    cursor: pointer;
}

.contents {
    clear: both;
    font-style: italic;
}

Here is memo-tempate.html:

<div class="memo">
  <div class="title">${this.title}</div>
  <div class="close" dojoAttachEvent="onClick">X</div>
  <div class="contents" dojoAttachPoint="containerNode"></div>
</div>

Creating a Sample Application:

Here we created a simple sample application that makes use of our new widget:

<html>
<head>

<title>Dojo Widget Test</title>

<script type="text/javascript" src="dojo/dojo.js"></script>
<script language="JavaScript" type="text/javascript">
 dojo.require("coach.widget.*");
</script>
</head>
<body>

<h2>
Hello, this is my second widget instance:
</h2>

<div dojoType="coach:Memo" title="Reminder">
 Pick up milk on the way home
</div>
<h2>
Hello, this is my second widget instance:
</h2>
<div dojoType="coach:Memo" title="Coach's Reminder">
 Hello, please get to work
</div>
</body>
</html>

Note that the widget parameter “dojoType” uses a custom namespace “coach” , which is defined in “memo.js” file.

If you run the page memo.html, this is how it looks like:

Packaging the Widget:
The dojo loading system may require a package file that provides packaging and dependency information about our widget. Our __package__.js file is really simple:

dojo.kwCompoundRequire({
    common: [
        "coach.widget.Memo"
    ],
    browser: [    ]
});

dojo.provide("coach.widget.*");

Creating a Custom Build Profile:
Web application performance is influenced by the initial download size as well as the number of round trips. Packing a list of required files into one single download can significantly reduce the number of round trips. The goal of creating a custom profile is to optimize the application performance by deciding the tradeoff between the initial download size and the number of round trips.

Let’s start by creating a Dojo minimum build – this is the absolutely minimum initial download required by Dojo and thus naturally requires a high number of round trips. Issue the following command from a system shell:

ant –Dprofile=minimum clean release

Dojo building system will take care of all the necessary steps in creating a custom build based on this profile. The result is a single dojo.js file whose size is 23KB, which is a result of 6 files combined:

dojoGuardStart.js,
../src/bootstrap1.js,
../src/loader.js,
dojoGuardEnd.js,
../src/hostenv_browser.js,
../src/bootstrap2.js

Now let’s run memo.html, and capture the network requests that are sent by the browser to load additional files. The recorded network requests are:

release/dojo/dojo.js
release/coach/widget/__package__.js
release/coach/widget/Memo.js
release/dojo/src/widget/HtmlWidget.js
release/dojo/src/widget/DomWidget.js
release/dojo/src/event/__package__.js
release/dojo/src/event/common.js
release/dojo/src/lang/array.js
release/dojo/src/lang/common.js
release/dojo/src/lang/extras.js
release/dojo/src/lang/func.js
release/dojo/src/event/topic.js
release/dojo/src/event/browser.js
release/dojo/src/widget/Widget.js
release/dojo/src/lang/declare.js
release/dojo/src/ns.js
release/dojo/src/widget/Manager.js
release/dojo/src/a11y.js
release/dojo/src/uri/__package__.js
release/dojo/src/uri/Uri.js
release/dojo/src/html/common.js
release/dojo/src/dom.js
release/dojo/src/html/style.js
release/dojo/src/xml/Parse.js
release/dojo/src/html/util.js
release/dojo/src/html/layout.js
release/dojo/src/html/display.js
release/dojo/src/lfx/toggle.js
release/dojo/src/lfx/__package__.js
release/dojo/src/lfx/html.js
release/dojo/src/gfx/color.js
release/dojo/src/lfx/Animation.js
release/dojo/src/html/color.js

Though the initial download size of dojo.js is only 23KB, this profile requires 33 round trips in order to display the widget. This is obviously not optimal. We should create a profile that packs all the initially required files into a single file so that all of them can be download within one request/response cycle.

After some experimentation, our custom build profile, coach.profile.js, is here:

var dependencies = [
    "coach.widget.Memo"
];

dependencies.prefixes = [
   ["coach", "release/coach"]
];

load("getDependencyList.js");

Now let’s do a build by:

ant –Dprofile=coach clean release

This produces a new dojo.js that has packed all necessary files into one single file. The footprint of this custom dojo.js is 168KB. The files that are packed into this build are:

dojoGuardStart.js,
../src/bootstrap1.js,
../src/loader.js,
dojoGuardEnd.js,
../src/hostenv_browser.js,
../src/bootstrap2.js,
../src/lang/common.js,
../src/lang/array.js,
../src/lang/func.js,
../src/lang/extras.js,
../src/event/common.js,
../src/event/topic.js,
../src/event/browser.js,
../src/event/__package__.js,
../src/widget/Manager.js,
../src/dom.js,
../src/widget/Parse.js,
../src/lang/declare.js,
../src/ns.js,
../src/uri/Uri.js,
../src/uri/__package__.js,
../src/html/common.js,
../src/a11y.js,
../src/widget/Widget.js,
../src/html/style.js,
../src/xml/Parse.js,
../src/widget/DomWidget.js,
../src/html/display.js,
../src/html/layout.js,
../src/html/util.js,
../src/gfx/color.js,
../src/lfx/Animation.js,
../src/html/color.js,
../src/lfx/html.js,
../src/lfx/__package__.js,
../src/lfx/toggle.js,
../src/widget/HtmlWidget.js,
../release/coach/widget/Memo.js

If you compare the list of files that are packed into this dojo.js and the list of files that are packed into the minimum build profile as well as the subsequent network requests, they are identical. So we achieved the goal of creating a custom build profile that packaged all the necessary files, but only the necessary files, into the initial download. Now, with one request, the browser can display our widget without the need to request additional JavaScript files from the server.

Summary:
This sample application that uses this simple "memo" widget can be accessed here: http://www.ajaxword.com/dojowidget/release/memo.html.

The code (including Dojo) can be downloaded from http://www.ajaxword.com/dojowidget/release/dojowidget.zip. (37MB)

After download, unzip the "dojowidget.zip" - it is basically the Dojo 0.4 package with a few file additions. So you should be able to run build scripts etc following the Dojo 0.4 instructions. .

The entire source code (memo.js, template files, package files and CSS files) for the "memo" widget is located under "release/coach " directory. The sample application "memo.html " is located at "release " directory too.

The build profile file "coach.profile.js " is located at "buildscripts\profiles " directory.

If you have questions, feel free to post them as comments here . Have fun!

  1. 2 Responses to “Dojo Custom Widget Tutorial”

  2. I would create my own directory structure and my own JavaScript package – all separate from Dojo code so that I can easily maintain my own code without being intermixed with Dojo.

    By Anonymous on Jun 11, 2007

  3. I would create my own directory structure and my own JavaScript package – all separate from Dojo code so that I can easily maintain my own code without being intermixed with Dojo.
    Dealers

    By nitinkr11 on Jun 11, 2007

Post a Comment