Dojo: Asynchronous Module Definition (AMD)

January 23, 2012

Tested with: Dojo Toolkit 1.7.1

From version 1.6, the Dojo team started the refactoring of Dojo toolkit in order to make it compliant with the CommonJS AMD API. AMD allows to create more efficient scripts, to use different toolkits in the same web application avoiding conflicts and much more. In version 1.7, the refactoring has been completed and this release is also the base to start the migration to Dojo 2.0.

In this post, I explain how to migrate old code to the new style exploiting AMD module format.

Configuration

First of all we have to configure Dojo in order to use asynchronous features.

<script type="text/javascript">
    var dojoConfig = {
        baseUrl: "js",
        isDebug: true,
        parseOnLoad: false,
        // enables AMD loader
        async: true,
        // modulePaths parameter is deprecated
        packages: [
            {
                name: "dojo",
                location: "dojo"
            },
            {
                name: "dijit",
                location: "dijit"
            },
            {
                name: "foo",
                location: "info/eliacontini"
            }
        ]
    };
</script>
<script type="text/javascript" src="js/dojo/dojo.js"></script>

The above configuration works with the following directories layout.

project/
css/
img/
js/
    dijit/
    dojo/
    dojox/
    info/
    eliacontini/
        FooWidget/
        nls/
            en/
            lang.js
            lang.js
        template/
            FooWidget.html
        FooWidget.js
index.html

After we configured Dojo, we are able to call our FooWidget and to use it in our web application.

<script type="text/javascript">
    require(["foo/FooWidget"], function(Foo) {
        var someVar = new Foo();
        /* ... do something with someVar ... */
    });
</script>

Writing custom widgets

Now we have to adapt our FooWidget to the AMD module format.

define([
    /* dijit/_Widget is deprecated */
    "dijit/_WidgetBase",
    /* dijit._Templated is deprecated and
    will be removed in Dojo 2.0 */
    "dijit/_TemplatedMixin",
    // declare function
    "dojo/_base/declare",
    // load template
    "dojo/text!./FooWidget/template/FooWidget.html",
    // load i18n strings
    "dojo/i18n!./FooWidget/nls/lang"
], function(_Widget, _Templated, declare, template, i18n) {
    return declare("info/eliacontini/FooWidget", [_Widget, _Templated], {
        templateString: template,
        _i18n: i18n,

        constructor: function() {
            console.log(this.templateString);
            console.log(this._i18n.test);
        }
    });
});

In the template file remember to use HTML5 Data Attributes!

Widgets in Template

If in our template we plan to use other widgets in the declarative way, we have to extend also the dijit/_WidgetsInTemplateMixin class.

define([
    // ...
    "dijit/_WidgetsInTemplateMixin"
    // ...
], function(/*...,*/ _WidgetsInTemplate /*, ...*/) {
    return declare(
        "info/eliacontini/FooWidget",
        [_Widget, _Templated, _WidgetsInTemplate],
        {
            // ...
            widgetsInTemplate: true
            // ...
        }
    );
});

Internationalization (I18N)

In the default language file, we have to specify the default translation and which are the available translations.

This is the default language file.

define({
    root: {
        test: "test root"
    },
    en: true // English translation available
});

In the en directory the file contains:

define({
    test: "test en"
});

That's all!


A photo of Elia Contini
Written by
Elia Contini
Sardinian UX engineer and a Front-end web architect based in Ticino, Switzerland. Marathoner, traveller, wannabe nature photographer.