understanding dojo AMD loading- functions are undefined

1.3k views Asked by At

I have been trying to get someone to explain to me how the dojo AMD loading works and to get a simple piece of code to work. I understand that if using for example the CDN, one has to call the dojo library and load all modules you wish to use. I have tried to implement other javascript functions based on activity from the main page and I will always get the function either undefined or an error related to a dojo control undefined. It seems that all the modules that initially load are not available to the rest of the code. Any helpful explanations would be really appreciated.

          <link rel="stylesheet" type=
        "text/css" href="http://ajax.googleapis.com/ajax/libs/dojo/1.8.4/dojo/resources
                           /dojo.css" />
          <link rel="stylesheet" type=
        "text/css" href="http://ajax.googleapis.com/ajax/libs/dojo/1.8.4/dijit/themes/
                           tundra/tundra.css" />
          <link rel="stylesheet" type=
        "text/css" href="http://ajax.googleapis.com/ajax/libs/dojo/1.8.4/dojox/mobile/themes/
                           iphone/iphone.css" />
         <title> DOJO </title>
         <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/dojo/1.8.4/
                   dojo/dojo.js" 
              data-dojo-config="async:true"></script> 

      <script type="text/javascript" src="Scripts/login.js"></script>
     <script type="text/javascript">
      require(["dojox/mobile/parser",
                "dojo/parser",
                "dojo/on",
                "dojo/request/xhr",
                "dijit/form/Form",
                "dojo/store/Observable",
                "dojo/store/Memory",
                "dijit/Toolbar",
                "dijit/Dialog",
                "dojo/io/script",
                "dojo/query",
                "dojo/_base/lang",
                "dijit/layout/ContentPane",
                "dojox/mobile/Button",
                "dojox/mobile/deviceTheme",
                "dojox/mobile/compat",
                "dojox/mobile/Heading",
                "dojox/mobile/TextBox",
                "dojox/mobile/Opener",
                "dijit/form/TextBox",
                "dijit/form/HorizontalSlider",
                "dijit/form/ValidationTextBox",
                "dijit/Calendar",
                "dojox/mobile/ScrollableView",
                "dojo/dom",
                "dojo/domReady!",
                "dojox/mobile"],

        function (dom, domReady ,mobile, ScrollableView, 
               parser, query, domClass, domStyle, on, event, xhr,Form,
             lang, Button, deviceTheme, compat, Heading) {
            dojox.mobile.parser.parse();
        });

</script> 

From my understanding is that the way I have the code above is that my interface will load correctly and all widgets in the body of html will be displayed and it works fine. The problem is that I have a form that gets input from the user and on a button click event calls a function that handles the webrequests. I could not get this to work and it is merely a problem with where I am placing this function. I have added a simplified version:

What I have done is add that function to a script file to separate it from the rest of the code:

var dojoXhr;

function correctInput(div, td, msg) {
dojo.domStyle.set(div, 'display', '');
td.innerHTML = msg;
}

require(["dojo/_base/declare", "dojo/parser", "dojo/query", "dojo/dom-class", 
     "dojo/dom-style",   "dojo/on", 
     "dojo/_base/event",
     "dojo/request/xhr", "dijit/form/ValidationTextBox", "dojo/domReady!"], 
  function chklogin(declare, parser, query, dom-class, dom-style, 
      on, event, xhr,ValidationTextBox, domReady) {

   var lname = dijit.byId('login').get('value');
   var psswd = dijit.byId('password').get('value');
   var feedback = document.getElementById('feedback');
   var feedbackTD = dojo.query('td.feedback')[0];
   if (lname == '' || psswd == '') {
       correctInput(feedback, feedbackTD, 'Please enter a valid login!');
       dojo.domStyle.set(feedback, 'display', '');
       dojo.domStyle.set(document.getElementById('msgBodyOutter'), 'display', 'none');
       feedbackTD.innerHTML = "Please enter a valid login!";
       return;
   }
   if (!(lname == 'login') || !(psswd == 'password')) {
       correctInput(feedback, feedbackTD, 'Please enter a valid login!');
       return;
   }
   else {
       dojo.domStyle.set(feedback, 'display', '');
       dojo.domStyle.set(document.getElementById('msgBodyOutter'), 'display', 'none');
       feedbackTD.innerHTML = "THATS IT BRO!";
       return;
   }


});

I got advice on the dojo forum to put my function in a define function and then use a require to call it all. I could not figure out how to do this.

1

There are 1 answers

5
medokr On BEST ANSWER

It seems that all the modules that initially load are not available to the rest of the code.

You are using a CDN to load the dojo toolkit. When you use CDN you are required to define the location of the module packages. You need to edit the dojoConfig for the code to work.

See this article about Using Custom Modules with a CDN. The important part is the packages object.

<script data-dojo-config="async: 1, dojoBlankHtmlUrl: '/blank.html',
        packages: [ {
            name: 'custom',
            location: location.pathname.replace(/\/[^/]+$/, '') + '/js/custom'
        } ]"
    src="//ajax.googleapis.com/ajax/libs/dojo/1.9.1/dojo/dojo.js">
</script>

Edit: Below is a simple dojo application.

So in my case create a module called chklogin, then require it, and when the user clicks the button it will call that module chklogin from within the main require[] function. Correct?

I would say yes. You are correct. I think your concept is a viable option. I hope this example helps with implementing define() to create your own modules. I will try to help where I can as you develop your idea. You can download the project here while available.

Directory Structure:

/index.html
/js/config.js
/js/controller/Controller.js
/js/modules/MyFirstModule.js

/index.html

<!doctype html>
<html>
    <head>
    <meta charset="UTF-8">
     <title>Demo</title>
    <link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/dojo/1.9.1/dijit/themes/claro/claro.css">
    <script src="js/config.js"></script>
    <script src="http://ajax.googleapis.com/ajax/libs/dojo/1.9.1/dojo/dojo.js"></script>
    <script>
        require(["app/Controller", "dojo/domReady!"], function(Controller) {

            //Initiate the entire application by calling main method of our Controller class. 
            Controller.main();

            //Call our getter method of the Controller class to show how to access a private variable.
            console.log(Controller.getWelcomeMessage());
        });
    </script>
    </head>

    <body class="claro" id="appBody"></body>
</html>

/js/config.js

We use packages to reference the CDN dojo files. Now we can call dojo classes by our package name For example, "dojo/domReady!", "dijit/form/Button", "dojox/app/main". The dojo files are stored on the google servers, which is referenced by the <script src='http://ajax.googleapis.com/ajax/libs/dojo/1.9.1/dojo/dojo.js'>< /script> in the index.html file.

Here we create our own custom packages. This could be for your modules, widgets, etc. The package locations will map to the javascript directory that you store your custom dojo files in. For example, myModules can be found in the /js/modules directory. You will reference any custom dojo files via "myModules/MyModule", which locates and loads "/myModules/MyModule.js" file.

For an explanation of the baseURL, see: http://dojotoolkit.org/documentation/tutorials/1.9/hello_dojo/ "Defining and Requiring Modules". This code registers the correct location of our own packages so we can load Dojo from the CDN whilst still being able to load local modules.

I created a package called "app" as you can see below. This is how I initialize my app in my project. This was designed to allow me to keep the separation of code the best I know how. It is loaded and called in the index.html page. So i give it a package name of app. It is physically located in the js/controller/Controller.js file.

This dojoConfig object is used in the index.html and must be loaded prior to < script src='...dojo.js' > tag.

var dojoConfig = {
    async: true,
    tlmSiblingOfDojo: false,
    baseUrl: location.pathname.replace(/\/[^/]*$/, ''),
    packages: [
        { name: "myModules", location: "js/modules" },
        { name: "app", location: "js/controller", main: "Controller" }
    ]
};

if you choose to host the dojo files on your own server, you can reference them like below. Assuming the dojo js files are located in the "/js/dojo/*" directory.

packages: [
        { name: "dojo", location: "dojo/dojo" }, 
        { name: "dijit", location: "dojo/dijit" },
        { name: "dojox", location: "dojo/dojox" },
        { name: "myModules", location: "js/modules" },
        { name: "app", location: "js/controller", main: "Controller" }
    ]

/js/controller/Controller.js

Here is the controller which I use to initialize the web app.

define(["myModules/MyFirstModule"], function(MyFirstModule) {

    //Private Variables...
    var privateVariable1 = "Welcome to my Dojo Application!";
    var privateVariable2;

    /**
     * init. This is a private function that is only available within this object.
     */
    init = function() {
        // proceed directly with startup
        console.log("Startup functions are firing...");

        //Render our "form" which only contains a single text box.
        renderForm();
    },

    renderForm = function() {
        MyFirstModule.createForm("appBody");
    }

    /**
     * Enclose all public methods in the return object
     */
    return {

        /**
         * main. This is a public function that can be called from other code.
         */
        main: function() {

            //Run init() method.
            init();
        },

        /**
         * getWelcomeMessage. This public function returns the value of the privateVariable1.
         * This mimics a getter method.
         */
        getWelcomeMessage: function() {
            return privateVariable1;
        }
    };

}); //end define

/js/modules/MyFirstModule.js

This is an example of a custom Module. It is required by the Controller class as a dependency.

define([
    //The required dependencies for this module.
    "dojo/dom", "dojo/on", "dijit/form/TextBox", "dijit/form/Button"
], function(dom, on, TextBox, Button){
    // Once all modules in the dependency list have loaded, this
    // function is called to define the myModules/myFirstModule module.
    //
    // The dojo/dom module is passed as the first argument to this
    // function; additional modules in the dependency list would be
    // passed in as subsequent arguments (on, TextBox, and Button).

    // Private variables
    var firstNameTextBox;
    var submitButton;

    privateFunction = function() {
        console.log("I am a private function. I can only be called from this class.");
    };

    // This returned object becomes the defined value of this module when called elsewhere.
    return {
        /**
         * createForm. This method creates a simple form. Textbox and button.
         * @param placeMeHere This is where to place the form elements. In this demo, the are placed in the 
         * body of the html document. This is executed in the Controller class.
         */
        createForm: function(placeMeHere) {

            //Create new TextBox.
            firstNameTextBox = new TextBox({
                name: "firstname",
                value: "" /* no or empty value! */,
                placeHolder: "type in your name"
            }, "firstname");

            //Place me in the DOM.
            firstNameTextBox.placeAt(placeMeHere);

            //Render 
            firstNameTextBox.startup();

            //Create Button
            submitButton = new Button({
                label: "Say Hi"
            }, "submitButton");
            submitButton.placeAt(placeMeHere);
            submitButton.startup();

            //Greet the user.
            on(submitButton, "click", function(evt){
                console.log("Hi there, " + firstNameTextBox.get("value"));
            });

        }
    };
});