Thursday 6 September 2012

Dojo / Dijit / Dojox Tips

Dojo is the javascript abstraction and extension library written by IBM.  It is less popular than JQuery but has superior functionality in some regards, particularly surfaces and shapes.

Documentation

Entry Point: http://dojotoolkit.org/
Latest Reference Guide: http://dojotoolkit.org/reference-guide/
Latest API Docs: http://dojotoolkit.org/api/

GFX / SVG Tutorial (version number in link): http://dojotoolkit.org/documentation/tutorials/1.8/gfx/
GFX / SVG Reference: http://dojotoolkit.org/reference-guide/1.8/dojox/gfx.html

Widget Test Areas
http://archive.dojotoolkit.org/nightly/dojotoolkit/
(navigate to the module required then 'test' for the html page)
Eg,
http://archive.dojotoolkit.org/nightly/dojotoolkit/dijit/tests/form/test_Button.html
http://archive.dojotoolkit.org/nightly/dojotoolkit/dojox/form/tests/test_TriStateCheckbox.html

Requires Structure

Dojo uses a particular structure to define the packages which are going to be used.  This is easiest to show by example,

    <script type="text/javascript">
        require(["dojo/on", "dojo/ready", "dojo/domReady!"],
            function(on, ready) {
                ready(function() {
                    on(dojo.byId("myButton"), "click", function(evt) {
                        alert("button clicked");
                    });
                });
            }
        );
    </script>

Here the dojo packages are defined in an array as the first parameter passed to the require() function.  in the second parameter a function is defined which itself has parameters with names marrying up to the the packages imported.  Eg

    dojo/on  ->  on
    dojo/ready  -> ready

These lists can get quite big at times!

Modules

Splitting code into multiple 'modules' like dojo can be really useful.  The main code that does this is,

    <script type="text/javascript">
    dojoConfig = {
       async: true,
       isDebug: true,
       parseOnLoad: true,
       packages: [{
           name: "example",
           location: '${pageContext.request.contextPath}/resources/js/modules'
       }]
    }
    </script>

Here the ${pageContext.request.contextPath} is used rather than <c:url ...> and that is because if the server doesn't know whether cookies are supported it'll add a ;jsessionid=... on the end of the c:url and this will stop dojo resolving the modules correctly.  Therefore using the c:url request here is not a good idea.

If a file utils.js is placed in the 'location' then this can be used by referencing example/utils in the require method.

    <script type="text/javascript">
        require(["example/utils", "dojo/ready", "dojo/domReady!"],
            function(exampleUtils, ready) {


The actual utils.js file has a very similar structure to a basic dojo require except that it is a 'define' instead,

    define(["dojo", "dojo/dom", "dojo/dom-geometry", "dojo/dom-style"],
        function(dojo, dom, domGeom, domStyle) {
            variable1 = null,
            array1 = new Array(),
            arraySize = new function(){
                return array1.length;
            }
        
            return {
                arraySize : function arraySize(){
                    return arraySize();
                }
            };
        }
    );

Here the function of the module returns an object which has a method in it.  Making a call on the method in this returned object would cause a redirect to the method defined in the module itself.  This way internal 'private' functions can be created without them having scope in the return part of the module.

While the example above is fine it doesn't define an object that could be reused multiple times such as a canvas.  The example is for a module such as utils etc.

This following example has an extra layer of objects which encapsulate data

    define(["dojo", "dojo/dom", "dojo/dom-geometry", "dojo/dom-style"],
        function(dojo, dom, domGeom, domStyle) {
            myObject = {
                innerVariable : null,
                innerArray : new Array(),
                arraySize : new function(){
                    return innerArray.length;
                }
            }
        
            return {
                arraySize : function arraySize(){
                    return myObject.arraySize();
                }
            };
        }
    );

Note that object literals are used here now as we are defining the elements of the object.

Dojo style

To style items such as a button declaratively,

Watch out for the dojo version number, here it is 1.9.3
- Include the Dojo stylesheets
- Tell dojo to parse the html on load
- Include the Dojo .js file
- Set the style on the body

<html>
  <head>
    <!-- Stylesheets -->
    <link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/dojo/1.9.3/dojo/resources/dojo.css">
    <link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/dojo/1.9.3/dijit/themes/claro/claro.css">
    <link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/dojo/1.9.3/dojox/grid/resources/claroGrid.css">


    <!-- Parse on load -->

    <script>dojoConfig = {parseOnLoad: true}</script>


    <!-- Include .js -->
    <script src="http://ajax.googleapis.com/ajax/libs/dojo/1.9.3/dojo/dojo.js"></script>
  </head>

  <!-- Body class -->
  <body class="claro">

    <!-- Dojo type -->
    <Button id="myButton" data-dojo-type="dijit/form/Button">My Button</Button>
  
  </body>
</html>

byId

byId() is the main way to get an html element.

    var myComponent = dom.byId('myComponentId');

this will get the typical html element (the DOM node) where 'dom' is from the module "dojo/dom".
(see http://dojotoolkit.org/reference-guide/dojo/dom.html)

If you are using dijit to create widgets then the id specified in the original html can be used to get the javascript object rather than the HTML element.  As part of dijits creation of objects it creates a registry which links the original id to the appropriate javascript object that dijit creates.  This registry can be accessed using the module "dijit/registry" in the require or define call.  Then you simply do

    var myComponent = registry.byId('myComponentId');

(see http://dojotoolkit.org/reference-guide/dijit/registry.html)

While dojo.byId() and dijit.byId() exist they have been deprecated to the functions above.

Changing Style

For simple html elements this is really straight forward,

    domStyle.set("myHtmlElementId", "display", "block");
    domStyle.set("myHtmlElementId", "width", 100);

for dijit created items like dijit buttons this is harder because the id isn't going to be correct as dojo will have expanded the html

    require(["dojo/ready", "dojo/dom-style", "dijit/registry"],
        function(ready, domStyle, registry){
            ready(function(){
                // hide a widget with id="myThinger"
                domStyle.set(registry.byId("myThinger").domNode, "display", "none");
        });
    });

No comments:

Post a Comment