Search code examples
javascriptdojoamd

Trouble with Dojo Build and Conversion to AMD


I'm working on a Dojo application written by someone else. I'm new to Dojo, but have discovered that to reduce http requests i need to produce a "build" version. This i have done, AFAICT (i get a single compressed script at least), but when using the built script, none of the functions previously available work (they're "undefined").

Furthermore, in trying to figure this out, it looks as though this would be a good point to make the code AMD compatible (is this code AMD compatible?). How would this work with examples such as the one below? From reading it appears that i might need to make each existing function from scripts like this into a module, which would produce hundreds of scripts, and doesn't feel right. How best to convert code such as this so that it will be AMD compatible and buildable?

I've got 15 or so .js scripts all containing varying numbers of functions written this way...

TIA!

var somethingStatus = false;

somethingInit();

function somethingInit() {

    require(["dojo/ready", "dojo/dom", "dojo/dom-construct", "dojo/cookie", "dojo/json", "dojo/domReady!"], function(ready, dom, domConstruct) {

        ready(function() {

            var content = '';
            // content generated here, then...

            domConstruct.place(content, dom.byId('body'));

        });

    });

}

function somethingToTop(target) {

    require(["dojo/dom", "dojo/dom-style", "dojo/_base/fx", "dojo/window", "dojo/domReady!"], function(dom, domStyle, fx, win) {

        var vs = win.getBox();

        somethingBarStatus = true;

        fx.animateProperty({
            node: dom.byId('somethingBar'),
            properties: {
                top: { start: domStyle.get('somethingBar', 'top'), end: 0 },
                height: { start: domStyle.get('somethingBar', 'height') + (domStyle.get("somethingBar", "padding") * 2), end: vs.h }
            },
            duration: 500,
            onEnd: function() {
                document.location = 'http://192.168.0.1' + target;
            }
        }).play();

    });

}

function somethingEmptyTop() {

    require(["dojo/dom", "dojo/dom-construct", "dojo/domReady!"], function(dom, domConstruct) {

         globalContainerEmpty('somethingTop'); // this function is in another .js file, constructed similarly to this

    });

}

// many more functions like this below and across other scripts!

Solution

  • You're running into a common problem when migrating from pre-AMD Dojo to Dojo 1.7, in that many people would attempt to run scripts through the build that are entirely not modules. The Dojo build tool is really designed to accommodate modules, not loose scripts, but this sort of thing happened to "Just Work" before.

    In the example above, you have a script that seems to just define a number of global functions. If anything, it's the inverse of a proper module, since each individual function involves its own require call.

    When the Dojo build encounters a file that it detects isn't already in AMD format, it puts an AMD wrapper around it, with the purpose of adapting proper legacy Dojo modules that use dojo.provide, dojo.require, and global namespaces like dojo and dijit. The problem is, when these "global" functions in your script are wrapped, they become local to the define factory in the wrapper, and are thus no longer global.

    A proper conversion of the code above would look something like this:

    define([
        'dojo/_base/fx',
        'dojo/cookie',
        'dojo/dom',
        'dojo/dom-construct',
        'dojo/dom-style',
        'dojo/json',
        'dojo/window',
        'my/otherModule',
        'dojo/domReady!'
    ], function (fx, cookie, dom, domConstruct, domStyle, JSON, win, otherModule) {
    
        var somethingStatus = false;
    
        var util = {
            somethingInit: function () {
                var content = '';
                // content generated here, then...
    
                domConstruct.place(content, dom.byId('body'));
            }
    
            somethingToTop: function (target) {
                var vs = win.getBox();
    
                somethingBarStatus = true;
    
                fx.animateProperty({
                    node: dom.byId('somethingBar'),
                    properties: {
                        top: { start: domStyle.get('somethingBar', 'top'), end: 0 },
                        height: { start: domStyle.get('somethingBar', 'height') + (domStyle.get("somethingBar", "padding") * 2), end: vs.h }
                    },
                    duration: 500,
                    onEnd: function() {
                        document.location = 'http://192.168.0.1' + target;
                    }
                }).play();
            },
    
            somethingEmptyTop: function () {
                // assuming that the other module was converted similarly to this one
                otherModule.globalContainerEmpty('somethingTop');
            }
        };
    
        util.somethingInit();
        return util;
    }
    
    • Notice that all of the dependencies formerly in each individual function have been collected in the define call for this module.
    • dojo/ready is generally not needed with AMD since dojo/domReady! already waits for DOMContentLoaded (or equivalent), and the define factory already waits until modules are loaded.

    You would then be able to access each of the functions on this module by loading it via define in another module or require in a page script, then referencing the functions via the variable you store the module in. (In this example, otherModule is an example of using another converted module, since you suggested that globalContainerEmpty is in another similar script file.)

    The modules tutorial can hopefully provide further help, and possibly also the modern Dojo tutorial.