Hands-on Dijit with NumberSpinner

This section provides some hands-on usage for a pretty intuitive dijit—the dijit.form.NumberSpinner —to warm you up for the chapters that follow. First, we'll work through creating the dijit in markup, and then we'll follow up with programmatic creation.

Creating from Markup

As you learned from an earlier section on the parser, it's pretty trivial to stick a dijit into the page. You require in the resources, provide the dojoType attribute in a tag, and have the parser go to work. For completeness, Example 11-4 shows how we'd follow that very same pattern to instantiate a NumberSpinner dijit.

Example 11-4. Creating the NumberSpinner widget in markup

<html>
    <head>
        <title>Number Spinner Fun!</title>


        <link rel="stylesheet" type="text/css"
          href="http://o.aolcdn.com/dojo/1.1/dojo/resources/dojo.css" />
        <link rel="stylesheet" type="text/css"
          href="http://o.aolcdn.com/dojo/1.1/dijit/themes/tundra/tundra.css" />

        <script
            type="text/javascript"
            src="http://o.aolcdn.com/dojo/1.1/dojo/dojo.xd.js"
            djConfig="parseOnLoad: true"
        ></script>

        <script type="text/javascript">
            dojo.require("dojo.parser");
            dojo.require("dijit.form.NumberSpinner");
        </script>
    <head>
    <body class="tundra">
        <form> <!-- some really awesome form -->
            <input dojoType="dijit.form.NumberSpinner"
                constraints="{min:0,max:10000}" value=1000>
            </input>
            </form>
    </body>
</html>

Programmatic Creation

While you'll often create dijits in markup, programmatic creation is no less common and the process is the very same as creating any other Function Object because that's exactly what a dijit is—a Function Object. In general, the constructor for a dijit accepts two parameters. The first is an Object that provides properties that should be passed in, and these are the same properties that you would be including in the tag if you were creating in markup. The second parameter is a source node or the id for a source node that identifies the placeholder that the dijit should replace:

var d = new module.DijitName(/*Object*/props, /*DOMNode|String*/node)

Example 11-5 programmatically creates the NumberSpinner and produces the very same effect as Example 11-4.

Example 11-5. Programmatically creating the NumberSpinner widget

<html>
    <head>
        <title>Number Spinner Fun!</title>

        <link rel="stylesheet" type="text/css"
          href="http://o.aolcdn.com/dojo/1.1/dojo/resources/dojo.css" />
        <link rel="stylesheet" type="text/css"
          href="http://o.aolcdn.com/dojo/1.1/dijit/themes/tundra/tundra.css" />
        <script
            type="text/javascript"
            src="http://o.aolcdn.com/dojo/1.1/dojo/dojo.xd.js"
        ></script>

        <script type="text/javascript">
            dojo.require("dijit.form.NumberSpinner");
            dojo.addOnLoad(function(  ) {
                var ns = new dijit.form.NumberSpinner(
                    //props
                    {
                        constraints : {min:0,max:10000},
                        value : 1000
                    },
                    "foo" //node id
                );
                // do other stuff with ns here...

            });
        </script>
    <head>
    <body class="tundra">
        <form>
            <input id="foo"></input>
        </form>
    </body>
</html>

Lots of Niceties

This particular example displays a nice little text box with a control that allows you to adjust the value from either the arrow keys on the keyboard, by clicking the arrow controls on the dijit itself, or though manual keyboard entry. In any case, the min and max and key/value pairs are part of the constraints attribute that can be customized to form the upper and lower values for the spinner with the arrow keys or when clicking on the controls; the value attribute provides the default value just like in ordinary HTML. Manually changing the value via manual keyboard entry, however, still changes the value, which may trigger a tooltip-style error message. Figure 11-3 illustrates what happens.

Tip

Recall that Dojo attempts to supplement the existing fabric for developing web applications—not override it. Thus, common attributes for form elements such as the value attribute in the previous code listing's input element still work just like normal.

Left: a NumberSpinner dijit changing its value via keyboard or mouse control; right: the default display when manual keyboard entry enters a value that is out of bounds

Figure 11-3. Left: a NumberSpinner dijit changing its value via keyboard or mouse control; right: the default display when manual keyboard entry enters a value that is out of bounds

Warning

While techniques from djConfig showed key/value pairs expressing objects constructed in markup without the surrounding curly brackets like djConfig="parseOnLoad:true,isDebug:true", it is more the exception than the rule. Dijit requires that Object attributes in markup be expressed using braces like constraints="{min:0, max:100}".

You've probably already made the connection about the NumberSpinner 's keyboard entry and a11y, but there are some other niceties that are worth trying out right away. You've no doubt noticed the numeric formatting that is automatically applied to separate every three digits of numbers greater than 999. Note that if you were rendering the page for a supported locale that used a different separator for the digits, it would have happened automatically: locales like en-us use commas to separate values, like 1,000, while Spain, the es-es locale, for example, uses dots to separate the digits, like 1.000. Figure 11-4 demonstrates. Try it for yourself by modifying your default locale in djConfig. For example, to set a default locale of Spain, you could do the following:

djConfig="locale:'es-es'"

Warning

Remember that any values in djConfig that are strings need to contain additional quotes around them. The syntax for declaring an associative array inline makes this easy to forget, and unfortunately, the error messages that can result from forgetting it are not always helpful to point you in the right direction. Any djConfig settings need to be loading prior to Base bootstrapping.

Dijits handle special formatting for the supported locales right out of the box with no additional configuration required; the top NumberSpinner was configured with en-us while the bottom NumberSpinner was configured with es-es; the dijit internally took care of the formatting details

Figure 11-4. Dijits handle special formatting for the supported locales right out of the box with no additional configuration required; the top NumberSpinner was configured with en-us while the bottom NumberSpinner was configured with es-es; the dijit internally took care of the formatting details

Another great out-of-the-box feature that Dijit supports is the notion of controls being typematic—that is, they respond in special ways to holding down a mouse button or keyboard key. If you try holding down the mouse button on one of the controls for the NumberSpinner, you should notice that it gradually increases for the first 10 or so numbers and then eventually speeds up and moves rapidly. Not surprisingly, keyboard navigation works the very same way. The Page Up and Page Down buttons also work and adjust the values by multiples of 10 by default.

Defining Methods in Markup

In addition to being able to programmatically write ordinary JavaScript to control and extend widgets, Dojo also provides the ability to define the JavaScript directly in markup by including a special type="dojo/method" attribute in SCRIPT tags. This can be very convenient for designers as well as anyone who needs to rapidly prototype a new idea. What's especially notable about defining methods in markup is that the keyword this refers to the containing widget, so you have instant access to the context you'd expect.

Consider the following update to the example code listing that defines methods in markup:

<!-- snip -->

    <script type="text/javascript">
            dojo.require("dojo.parser");
            dojo.require("dijit.form.NumberSpinner");
            dojo.require("dijit.form.Button");
     </script>
    </head>
    <body class="tundra">
        <form>
            <div dojoType="dijit.form.NumberSpinner" jsId="mySpinner
"

                constraints="{min:0,max:10000}" value=1000>
                <script type="dojo/method">
                    dojo.mixin(this, {
                        reset : function(  ) { this.setValue(1000); }
                    });
                </script>
            </div>
        </form>
        <button dojoType="dijit.form.Button" onClick="mySpinner.reset(  )">
reset</button>
    </body>
</html>

To sum up the effect of the changes, the jsId attribute gave the NumberSpinner a global variable name mySpinner, which was referenced in a Button's onClick method. The actual body of the reset method was established by the special script tag included inside of the dijit. The script tag providing anonymous dojo/method is executed after its constructor runs, so that any values passed in via attributes included in markup would be available.

Also, note that whereas the previous listing used an input element to create the spinner, the new listing uses a div tag. The reason why an input tag will not work with the updated listing is that it does not allow innerHTML. The tag had to be switched to a type that does allow innerHTML in order for it to work. If you're wondering why a div tag wasn't used all along, it really comes back to one primary issue: the ability to have a semantically correct page that works without any JavaScript involved. In other words, the previous form using an input element is a semantically correct input control even if JavaScript were disabled (for whatever reason), whereas the div -based spinner is not. Most of the time, this isn't an problem, but when designing a degradable page, it is very important to know your basic HTML and be aware of these issues.

Warning

The dojo/method and dojo/connect script tags do not work inside of marked up elements that do not allow innerHTML. This isn't a Dojo thing; it's in accordance with the HTML specification. Although not demonstrated with this example, SCRIPT tags containing a type="dojo/connect" attribute allow you to set up connections in markup using the same pattern.

While the additional reset button may make a great addition for a mouse-based control, note that pressing the Escape key on the keyboard would have reset the spinner to its original value without any additional work at all.

As an improvement that produces the very same effect but with less code, consider the following change to the dojo/method script tag:

<script type="dojo/method" event="reset">
    this.setValue(1000);
</script>

Instead of being executed automatically a single time after the constructor, which is the case for anonymous dojo/method script tags, this approach performs the work of actually creating the reset method and attaching it to the widget on your behalf. If there had been arguments involved, an additional args attribute could have been specified. For example, args="foo,bar,baz" would have allowed for passing in three named arguments to a method defined in markup.

Get Dojo: The Definitive Guide now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.