Chapter 13. Form Widgets
This chapter provides systematic coverage of the various dijits
that enable you to create fantastic-looking forms with minimal effort.
Like everything in Dijit, the controls you'll learn about in this
chapter can be defined entirely in markup, require very little
JavaScript, and were designed with accessibility considerations in mind.
With that said, realize that you're about to embark upon reading a very
hefty chapter. The functionality offered by dijit.form is quite intense, packing tons of
breadth and depth; the form dijits are by far the most object-oriented
widgets in the toolkit, so you'll see deeper inheritance hierarchies via
dojo.declare in this chapter than
anywhere else in the book.
Drive-By Form Review
While the HTML 4.01 specification (http://www.w3.org/TR/html401/) provides the authoritative specification on forms and is quite worthy of careful reading on its own, this section attempts to summarize some of the most useful content about forms that will help you to get the most out of this chapter. If you're reading this book, a working assumption is that you've designed a form or two, so it is not necessary to belabor fact that a form is a collection of one or more controls that capture information and send it back to a server for processing.
However, it is noteworthy to highlight that the AJAX revolution
has really skewed the paradigm of passing data to a server for
processing. Previously, data would be submitted to a server handler
that broke it into convenient key/value pairs, used these hashes as
part of a processing routine, and then returned a new page that
somehow reflected these form choices. To make the whole process more
elegant, the form might have even been included in an iframe so that the effect of the page reload
would be minimized. Now, however, the XMLHttpRequest (XHR) object makes it easy to
asynchronously send small chunks of data to a server without an
explicit form submission or a page reload of any kind.
Of course, the XHR object, AJAX, and slicker ways of interacting with the user certainly don't make forms obsolete. Forms are still a battle-tested standard; they work even when JavaScript is disabled, and they are important for accessible implementations. In general, it's almost always a good idea to make sure that any fancy way of passing information back to the server is degradable and accessible. In other words, it isn't a matter of "either forms or AJAX"; it's a matter of "both forms and AJAX."
For example, consider Example 13.1, “Simple form”, an enhanced version of the plain vanilla form from Chapter 1, Toolkit Overview.
Example 13.1. Simple form
<html>
<head>
<title>Register for Spam</title>
<script type="text/javascript">
function help( ) {
var msg="Basically, we want to sell your info to a 3rd party.";
alert(msg);
return false;
}
//simple validation
function validate( ) {
var f = document.getElementById("registration_form");
if (f.first.value == "" || f.last.value == "" || f.email.value
== "") {
alert("All fields are required.");
return false;
}
return true;
}
</script>
<head>
<body>
<p>Just Use the form below to sign-up for our great offers:</p>
<form id="registration_form"
method="POST"
onsubmit="javascript:return validate( )"
action="http://localhost:8080/register/">
First Name: <input type="text" name="first"/><br>
Last Name: <input type="text" name="last"/><br>
Your Email: <input type="text" name="email"/><br>
<button type="submit">Sign Up!</button>
<button type="reset">Reset</button>
<button type="button" onclick="javascript:help( )">Help</button>
</form>
</body>
</html>While as bland as it can possibly get, this form is quite functional, and would behave properly on virtually any browser; the incorporation of a nice CSS stylesheet could make it look quite nice. There's even a Help button to tell the user why the form really exists. On the server side, a simple script would process the form, probably after a web server has already distilled the named fields in the form out of their raw format. A functional CherryPy script might process the form, as in Example 13.2, “CherryPy script to process a form”.
Example 13.2. CherryPy script to process a form
import cherrypy
class Content:
"""
A routine for processing a form submission.
Named form fields are easily accessible.
"""
@cherrypy.expose
def register(self, first=None, last=None, email=None):
#add user information to evil database here...
#send back this customized html page
return """
<html>
<head><title>You're now on our spam list!</title></head>
<body>
<p>Congratulations %s %s, you're gonna get spammed!</p>
</body>
</html>
""" % (first, last) #substitute in variables
cherrypy.quickstart(Content( ))While extremely simple, the previous example did touch on several fundamentals regarding forms:
Forms controls should be enclosed in a FORM tag.
The FORM tag almost always includes
name,method,onsubmit,enctype, andactionattributes that provide pertinent information about how the form should be processed.The
onsubmitattribute is the standard way of performing client-side validation. Returningfalsefrom a validation routine prevents the form from being submitted to the server.The
actionattribute provides the URL for submitting the form.Form fields that represent meaningful state should include a
nameattribute, which is what most server-side frameworks will collect into key/value pairs and pass to the specific routine that handles the form submission.Forms are innately accessible with the keyboard; tabs move between fields[24] and the Enter key submits the form. Although not demonstrated, the
tabindexattribute can change the default tab order.In general, there are multiple kinds of controls, as specified by the
typeattribute. This particular example illustrated three different kinds of buttons: one for triggering theonsubmitevent, one for resetting the form, and one for handling a custom action.Submitting a
formnecessarily reloads the page with whatever the server returns if anactionattribute is provided in the form. If noactionattribute is provided, custom JavaScript or DHTML actions could be taken by attaching scripts to DOM events such asonclick.
Tip
Throughout this chapter, the term "attribute" is frequently used to describe both form attributes and object attributes. The intended usage should be apparent from context and is not anything to get hung up over.
While nowhere near exhaustive, hopefully this brief review sets the stage for a discussion of the various form dijits. For a great desktop reference on HTML forms, consider picking up HTML & XHTML: The Definitive Guide by Chuck Musciano and Bill Kennedy (O'Reilly).
Form Dijits
Form dijits that are explicitly declared suitable for use in
bona fide HTML forms, as defined with the FORM tag, are a part of the dijit.form namespace. This section walks
through all of the dijits included in this namespace, providing
example code and sample screenshots that use Dijit's built-in
tundra theme. But first, it's worth reiterating
that all form dijits are designed to be fully degradable and
accessible; in other words, they remain fully functional even if
JavaScript, CSS, and images aren't available, and if a keyboard is the
only input device available. Accessibility attributes on Windows
environments also support high-contrast mode and screen
readers.
Figure 13.1, “The inheritance-rich dijit.form module” shows
the general inheritance structure of the dijit.form module. The diagram does not show
every single mixin class along the way, but does convey the general
relationships amongst the widgets. The hope is that you'll be able to
use it to get a better idea of how the source code is laid out when it
comes time to cut your teeth on it.
In addition to the standard dijit attributes inherited from
_Widget, such as domNode, et al., and ordinary HTML
attributes included in the HTML 4.01 spec, such as disabled and tabIndex, form dijits all inherit from a
base class that explicitly supports the attributes, methods, and
extension points listed in Table 13.1, “Supported attributes, methods, and extension points for form
dijits via _FormWidget”.
Table 13.1. Supported attributes, methods, and extension points for form dijits via _FormWidget
|
Name |
Data type |
Category |
Comment |
|---|---|---|---|
|
|
String |
Attribute |
The current value of the dijit; works just like its pure-HTML equivalent. |
|
|
String |
Attribute |
The named value for the dijit; works just like its pure-HTML equivalent; useful for form submissions to a server handler. |
|
|
String |
Attribute |
Alternate text that should appear should the browser not be able to display—a somewhat uncommon event for forms, although still common enough for images; works just like its pure-HTML equivalent. |
|
|
String |
Attribute |
Specifies the type of
the element when more than one kind is possible. For example,
a button might have |
|
|
Integer |
Attribute |
Used to provide an
explicit tab index for keyboard navigation; works just like
its HTML equivalent. By default, this attribute is |
|
|
Boolean |
Attribute |
Disables a control so
that it cannot receive focus and is skipped in tabbing
navigation; do not attempt to use this attribute on an element
that does not support it, which per the HTML 4.01 spec include
|
|
|
Boolean |
Attribute |
Disables a control so
that its value cannot be changed; however, it can still
receive focus, is included in tabbing navigation, and is
included in form submissions. Do not attempt to use this
attribute on an element that does not support it, which per
the HTML 4.01 spec include |
|
|
Boolean |
Attribute |
Whether to fire the
|
|
Function |
Method | The proper way to set
an attribute value for a dijit. For example, setting a dijit's
value attribute to
|
|
|
Function |
Method |
Sets the |
|
|
Function |
Method |
Returns information about whether the control can receive focus. |
|
|
Function |
Extension point |
By default, returns the
current state of the widget to be used for the WAI-ARIA
|
|
|
Function |
Extension point |
Override to provide a custom callback function that fires each time the value changes. |
Tip
For great online documentation on HTML 4.01 forms, see http://www.w3.org/TR/html401/interact/forms.html.
TextBox Variations
Ordinary text input via the HTML input element is by far the
most commonly used form field. Countless hours have been spent
formatting and validating what are generally small snippets of text,
and the auxiliary scripts that have supported input boxes may
single-handedly account for the most collective boilerplate that's
even been written to support web pages. If any one of those comments
resonates deep within your soul, the Dijit TextBox family will seem like a
godsend.
Let's take a look at each member of the TextBox family and improve our example form
from earlier in this chapter. The most basic member is the ordinary
TextBox itself, which comes packed
with several custom formatting operations as well as the ability to
create your own using the format
and parse extension points. The
following listing summarizes TextBox 's attributes and extension points.
A TextBox is technically a kind of
input element, so remember that the
standard HTML attributes, if not listed here, still apply.
Tip
TextBox 's attributes and
extension points are inherited by all other dijits in this family;
they are especially important to be aware of because they are widely
used.
TextBox
Table 13.2, “TextBox attributes and extension points”
provides a listing of pertinent features to the most basic
TextBox dijit.
Table 13.2. TextBox attributes and extension points
|
Name |
Category |
Comment |
|---|---|---|
|
|
Attribute |
Removes leading and
trailing whitespace. This attribute is |
|
|
Attribute |
Converts all
characters to uppercase. This attribute is |
|
|
Attribute |
Converts all
characters to lowercase. This attribute is |
|
|
Attribute |
Converts the first
character of each word to uppercase. This attribute is
|
|
|
Attribute |
Used for passing
through the standard HTML input tag's |
|
|
Extension point |
A replaceable
function to convert a value to a properly formatted
|
|
|
Extension point |
May be used to
provide a custom parsing function to convert a formatted
|
|
|
Method |
Used to set the
|
|
|
Method |
Used to fetch the
|
Warning
As of version 1.1, _FormWidget 's setValue and getValue methods were deprecated in
favor of using the setAttribute('value', /*...*/)
function for setting values and getting values via the .value property where appropriate.
TextBox, its subclasses, and
a few other dijits, however, override the setValue and getValue methods for legitimate use.
The rule of thumb is that setValue and getValue are used for widget
values. For example, a TextBox has an obvious value (hence,
the use of setValue and
getValue ), whereas you would
use the setAttribute method
for something like a Button because it does not have a widget
value even though a value is submitted with the form.
To illustrate the most basic usage possible, Example 13.3, “Updated form with TextBox and theming” plugs some text
boxes into our earlier form example, and switches on the propercase and trim attributes for the first and last fields in the form.
Example 13.3. Updated form with TextBox and theming
<html>
<head>
<title>Register for Spam</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
djConfig="parseOnLoad:true",
type="text/javascript"
src="http://o.aolcdn.com/dojo/1.1/dojo/dojo.xd.js">
</script>
<script type="text/javascript">
dojo.require("dojo.parser");
dojo.require("dijit.form.TextBox");
function help( ) {
var msg="Basically, we want to sell your info to a 3rd party.";
alert(msg);
return false;
}
//simple validation
function validate( ) {
var f = document.getElementById("registration_form");
if (f.first.value == "" ||
f.last.value == "" ||
f.email.value == "") {
alert("All fields are required.");
return false;
}
return true;
}
</script>
<head>
<body class="tundra">
<p>Just Use the form below to sign-up for our great offers:</p>
<form id="registration_form"
method="POST"
onsubmit="javascript:return validate( )"
action="http://localhost:8080/register/">
First Name:
<input dojoType="dijit.form.TextBox" propercase=true
trim=true name="first"><br>
Last Name:
<input dojoType="dijit.form.TextBox" propercase=true
trim=true name="last"><br>
Your Email:
<input dojoType="dijit.form.TextBox" length=25 name="email"><br>
<button type="submit">Sign Up!</button>
<button type="reset">Reset</button>
<button type="button" onclick="javascript:help( )">Help</button>
</form>
</body>
</html>Warning
If you try to use dijits without properly including the
dojo.css file and the
relevant theme, your dijits may still be accessible—but they'll
also look horrible. A common frustration with beginners to Dijit
is either forgetting to load the CSS or forgetting to set the
appropriate class attribute in the BODY tag.
In addition to the TextBox dijit improving the appearance
of the control, it also saves you the work of implementing a dozen
or so lines of custom scripting. Of course, you could override the
format extension point to implement your own custom formatting by
simply defining a JavaScript function and passing it into format. For example, the following
formatting function would take a string and turn it into
MiXeD CaPiTaLiZaTiOn like so:
function mixedCapitalization(value) {
var newValue = "";
var upper = true;
dojo.forEach(value.toLowerCase( ), function(x) {
if (upper)
newValue += x.toUpperCase( );
else
newValue += x;
upper = !upper;
});
return newValue;
}Using the function in the TextBox dijit is just as easy as it
should be:
<input dojoType="dijit.form.TextBox" format="mixedCapitalization"
trim=true name="first">If you interact with the form and cause a blur event by moving the cursor out of
it, you'll see the conversion take place. The parse function may be overridden in the
very same manner as format to
standardize values when they are returned. Common operations
include converting numeric types into Number values, or standardizing String values.
Tip
The custom format and
parse extension points are
invoked every time a setValue
or getValue operation is
called—not just in response to explicit user interaction with
the form.
ValidationTextBox
One thing that's probably on your mind is that pesky
validation function that ensures the fields are not empty—and the
fact that it wasn't all that great in the first place since it
didn't validate an email address properly. ValidationTextBox to the rescue!
Table 13.3, “Attributes of ValidationTextBox” includes a
complete listing of additional functionality that ValidationTextBox offers.
Table 13.3. Attributes of ValidationTextBox
Tip
The dijit.Tooltip widget
is covered in Chapter 15, Application Widgets.
Drop-in usage for a ValiationTextBox in our example is as
straightforward as adding required attributes to the various
controls and tacking on an additional regex to validate the email
address. The change in Example 13.4, “Updated form to use ValidationTextBox” incorporates a
ValidationTextBox and eliminates
the need for all of the JavaScript that was previously written; the
Help button was also removed now that a tooltip more elegantly
accomplishes that purpose.
Example 13.4. Updated form to use ValidationTextBox
<html>
<head>
<title>Register for Spam</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
djConfig="parseOnLoad:true",
type="text/javascript"
src="http://o.aolcdn.com/dojo/1.1/dojo/dojo.xd.js">
</script>
<script type="text/javascript">
dojo.require("dojo.parser");
dojo.require("dijit.form.ValidationTextBox");
</script>
<!-- lots of ugly JavaScript was removed -->
<head>
<body class="tundra">
<p>Just Use the form below to sign-up for our great offers:</p>
<form id="registration_form"
method="POST"
action="http://localhost:8080/register/">
First Name:
<input dojoType="dijit.form.ValidationTextBox"
properCase="true" trim=true required="true"
invalidMessage="Required." name="first"><br>
Last Name:
<input dojoType="dijit.form.ValidationTextBox"
properCase="true" trim=true required="true"
invalidMessage="Required." name="last"><br>
Your Email:
<input dojoType="dijit.form.ValidationTextBox"
promptMessage="Basically, we want to sell your info to a 3rd party."
regExp="[a-z0-9._%+-]+@[a-z0-9-]+\.[a-z]{2,4}" required
name="email"><br>
<button type="submit">Sign Up!</button>
<button type="reset">Reset</button>
<!-- tooltip message replaced need for help button -->
</form>
</body>
</html>And with very little effort, you suddenly have richer, better-looking functionality, with less code to maintain.
We still need to do some work to those buttons in the next
section, but first, let's work through the remaining members of the
TextBox family.
MappedTextBox and RangeBoundTextBox
Two well-defined form dijit classes that are not covered in
this chapter include MappedTextBox and RangeBoundTextBox. Basically, MappedTextBox provides some methods for
serializing its data into a String value via a custom toString method, and RangeBoundTextBox facilitates ensuring
that a value is within a specified range by allowing you to pass in
max and min values to the constraints object.
Although it might intuitively seem like the "validation" in ValidationTextBox should be handling tasks
like range checking, keep in mind that ValidationCheckBox uses regular
expressions to validate String
values. RangeBoundTextBox
explicitly deals with numeric types.
In short, these two classes provide some intermediate machinery that is used to enable the remaining form dijits in this chapter and are in place largely to facilitate the internal design. While you may want to be aware of these two classes if you plan on creating a highly custom form dijit, they are not really intended for general-purpose consumption.
TimeTextBox and DateTextBox
Custom validation routines for validating dates and times are
another implementation detail that just about any web developer who
has been around a while has had to produce at some point or another.
Although dates and times have well-defined formats that are quite
universal, the ultra-generic HTML INPUT element offers no support, and the
load is pushed off to JavaScript for validation and custom
formatting. Fortunately, Dijit makes picking dates and times just as
easy as it should be. These dijits are also preconfigured to work
with the most common locales, and extending them beyond the stock
locale collection is straightforward.
Tip
The DateTextBox and
TimeTextBox dijits use the
Gregorian calendar, which is the default for the dojo.date facilities.
Let's suppose that instead of spamming you, an organization would instead like to bother you over the telephone once you get home from a long, hard day of work. Naturally, they would like to collect information from you ahead of time so as to avoid any unnecessary overhead of their own. Assuming they're smart enough to be using Dojo to minimize costs on the programming budget, they might produce some form fields like so:
<!-- Remember to dojo.require these dijits before using them! --> Best Day to call: <input dojoType="dijit.form.DateTextBox"><br> Best Time to call: <input dojoType="dijit.form.TimeTextBox"><br>
That's it! No additional effort is required. The DateTextBox in Figure 13.2, “The DateTextBox pop up that appears” automatically pops
up a beautiful little calendar for picking a date when the cursor
enters the INPUT element, and a
scrolling list containing times, broken into 15-minute increments,
appears for the TimeTextBox in
Figure 13.3, “The TimeTextBox popup that appears”.
As a reminder, programmatic creation is just as simple:
var t = new dijit.form.TimeTextBox( ); var d = new dijit.form.DateTextBox( ); /* now place them in the page via their domNode attribute*/
In addition to ease of use, these dijits allow for customized
formatting of their displayed values—allowing you to do anything
that you could do via dojo.date,
which they use internally. Specifically, the formatLength, timePattern, and datePattern attributes may be specified
within the constraints object to
produce the corresponding effect.
Table 13.4, “Attributes for DateTextBox” and Table 13.5, “Attributes for TimeTextBox” summarize the various options available. In general, either the format length or one of the time or date patterns are specified, depending on the degree of granularity desired.
Table 13.4. Attributes for DateTextBox
|
Attribute |
Comment |
|---|---|
|
| Used to format a
value for the default locale. Valid values are
|
|
| Used to provide a custom format for all locales. Accepts a string formatted according to Java-like conventions. See http://www.w3.org/TR/NOTE-datetime. Common values with examples include:
|
|
|
When |
|
|
Allows for overriding
the default locale for this specific widget only. Be sure to
configure the extra local via |
|
|
When submitting a
form, the value of |
Table 13.5. Attributes for TimeTextBox
|
Attribute |
Comment |
|---|---|
|
|
A |
|
|
A |
|
|
A |
|
| A
|
|
| Used to provide a custom format for all locales. Accepts a string formatted according to Java-like conventions. See http://www.w3.org/TR/NOTE-datetime. Common values with examples include:
|
|
|
When |
|
|
Allows for overriding
the default locale for this specific widget only. Be sure to
configure the extra local via |
|
|
When submitting a form, the value of selector determines whether the date, the time, or both get passed with the submission, even though only a date or time is visible as a displayed value. By default, both are passed, specifying either date or time accordingly. |
In markup, the constraints
object is provided like any other attribute:
<input constraints="{datePattern:'MMM dd, yyyy'}" dojoType="dijit.form.DateTextBox">Just like always, the programmatic approach is a direct translation:
var d = new dijit.form.DateTextBox({datePattern:'MMM dd, yyyy'});Commonalities between DateTextBox and TimeTextBox
Two additional methods that are available for TimeTextBox and DateTextBox are getDisplayedValue and setDisplayedValue. The difference
between these methods and the ordinary getValue and setValue approaches involves the
difference in what is actually displayed in the dijit versus what
data type is used internally by the dijit. Both TimeTextBox and DateTextBox use JavaScript Date objects internally, and getting
this Date object is just one
method call away.
Recall that the machinery inherited from RangeBoundTextBox also allows for
min and max values to be provided, which is
highly useful for preventing a user from ever selecting an invalid
value from the pop up. For example, to constrain a date from
December 1, 2007 through June 30, 2008:
<input constraints="{min:'2007-12', max:'2008-06', datePattern:'MMM dd, yyyy'}"
dojoType="dijit.form.DateTextBox">Additionally, MappedTextBox wires in facilities for
serialization via the toString
method; you can also get an ISO-8601 compliant string if you
should need one, which can be quite useful for sending back to the
server.
Tip
It's important to understand the duality between datePattern, timePattern, and the ISO-8601
specification: basically, there isn't a connection. The datePattern and timePattern values are used for
opaquely manipulating user-visible formatting for widgets, while
the ISO-8601 formatting is what the parser accepts and sends to
the server for processing.
Two additional methods provided by these two dijits include
getDisplayedValue and setDisplayedValue. While setDisplayedValue produces the same
results as setAttribute('value',
/*...*/), getDisplayedValue returns the values you
see in the dijit, while resolving the dijit's .value property to return a JavaScript
Date object.
Table 13.6, “DateTextBox and TimeTextBox commonalities” provides a
quick synopsis of these additional features that both DateTextBox and TimeTextBox provide.
Table 13.6. DateTextBox and TimeTextBox commonalities
|
Name |
Comment |
|---|---|
|
|
Retrieves the
formatted value that is actually displayed in the form
element, whereas |
|
|
Sets both the
displayed as well as the internal value for the dijit.
(Calling |
|
|
Returns an ISO-8601-compliant date or time value. |
|
|
Provided to constrain the values that are available via the pop ups. |
|
|
An extension point
that can be used to specify a custom implementation for
the |
Serializing data to the server
As it turns out, the serialize extension point can be
especially useful when transferring data to and from a server-side
component that is expecting a date to be formatted in a special
way. For example, you might use the code in Example 13.5, “Custom serialization of data to the server with a
DateTextBox” to extend the DateTextBox and provide a custom format
when the toString method is
used. Example 13.5, “Custom serialization of data to the server with a
DateTextBox” illustrates
using a custom DateTextBox to
submit a custom value that is different from what is
displayed.
Example 13.5. Custom serialization of data to the server with a DateTextBox
<html>
<head>
<title>Custom DateTextBox</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
djConfig="parseOnLoad:false",
type="text/javascript"
src="http://o.aolcdn.com/dojo/1.1/dojo/dojo.xd.js">
</script>
<script type="text/javascript">
dojo.require("dojo.parser");
dojo.require("dijit.form.DateTextBox");
dojo.addOnLoad(function( ) {
dojo.declare("dtdg.CustomDateTextBox",[dijit.form.DateTextBox], {
serialize: function(d, options) {
return dojo.date.locale.format(d,
{
selector:'date',
datePattern:'dd-MMM-yyyy'}).toUpperCase( );
}
});
dojo.parser.parse(dojo.body( ));
});
</script>
</head>
<body class="tundra">
<form action="http://localhost:8080" type="POST">
<input dojoType="dtdg.CustomDateTextBox" name="customDate"/>
<input type="submit" value="Submit"/>
</form>
</body>
</html>A minimal CherryPy class can accept the form submission and display it for you:
import cherrypy
class Content:
@cherrypy.expose
def index(self, **kwargs):
return str(kwargs)
cherrypy.quickstart(Content( ))Don't forget about inherited properties
Although the inheritance hierarchy is getting a little bit
deep by this point, recall that all of the methods inherited from
TextBox and ValidationTextBox are also available to
use and are essential for many common use cases. A review of
dojo.date, as presented in
Chapter 6, Internationalization (i18n), is also
helpful for brushing up on some of the finer details associated
with these dijits.
NumberTextBox
NumberTextBox inherits
all of the great features you've grown to love from RangeBoundTextBox and its ancestors and
expands upon them with customization for numeric types via the
dojo.number facilities. In a
nutshell, numeric value formatting defaults to the current locale
and allows you to provide the constraints listed in Table 13.7, “NumberTextBox constraints”.
Table 13.7. NumberTextBox constraints
|
Name |
Comment |
|---|---|
|
|
Used to check the
bounds of the input, just like any other |
|
|
Used to provide the number of digits to require after the decimal, along with any additional formatting, such as a percent sign that follows. |
|
|
Used to designate
that the value should be a |
|
|
Used to designate the number of places to require after the decimal (providing this value in addition to a custom pattern overrides the pattern). |
For example, to require a value to have exactly two places after the decimal and a percent sign, the following does the trick:
<input constraints="{pattern: '#.##%'}" dojoType="dijit.form.NumberTextBox">Although there is only a single hash before the decimal
place, note that you can have multiple digits. Should you not want
any dijits before the decimal, however, you can provide a pattern
without a leading hash, such as {pattern:'.##%'}. Also note that when
editing begins, the displayed values automatically convert to a
pure numeric value; when editing ends, the value converts back to
a formatted number.
Recall that dojo.number
as presented in Chapter 6, Internationalization (i18n) is your
one-stop shop for tons of custom facilities for number formatting
and related operations. NumberTextBox directly builds upon these
facilities.
NumberSpinner
The NumberSpinner was
introduced in Chapter 11, Dijit Overview, and you can think
of the NumberSpinner and a
fancier NumberTextBox with
small buttons on the edge that allow for incrementally increasing
the value. The buttons are typematic in that you can hold them
down and they will repeatedly affect the value. The NumberSpinner also has slightly
different min and max constraints in that if min and max constraints are provided, the
NumberSpinner 's buttons will
not allow you to move outside of those boundaries.
NumberSpinner offers the
attributes listed in Table 13.8, “NumberSpinner attributes”.
Table 13.8. NumberSpinner attributes
|
Name |
Comment |
|---|---|
|
|
The number of
seconds a key or button is held down before it becomes
typematic. This attribute is |
|
|
The fraction of
time that is used to change the typematic timer between
events. A value of |
|
|
The value to adjust
the spinner by when using arrow keys or buttons. This
attribute is |
|
|
The value to adjust
the spinner by when using the Page Up or Page Down keys.
This attribute is |
Creating a NumberSpinner
is just like creating any other dijit:
<input dojoType="dijit.form.NumberSpinner" smallDelta="2" largeDelta="4"
constraints="{min:100,max:120}" value="100">CurrencyTextBox
The CurrencyTextBox is
the farthest dijit from the common ancestor, inheriting from
NumberTextBox, and utilizes
dojo.currency for much of its
formatting handiwork.
This dijit, however, provides only one additional attribute,
currency, which is formatted
according to its specific locale. Values for currency must be one of the three letter
sequences specified in the ISO4217 currency code standard,
available from http://en.wikipedia.org/wiki/ISO_4217.
Warning
Anytime international characters such as currency symbols are used, you'll want to be especially aware of the encoding that your browser is using so that all symbols are rendered properly. There is always the possibility that the web server may not include this information in the header.
In HTML pages, the standard way of specifying an encoding
is by placing a special META
tag in the head of the page, and the Dijit project encourages
this technique as a best practice. The following example is a
META tag for the UTF-8
character set, which is almost always a safe bet:
<META http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
Note that as of version 1.1, you will need to use this tag if serving up Dojo from AOL's CDN because the server currently does not include encoding information in the headers, which is another means of achieving the same result. (Otherwise, currency and certain unicode symbols may not display properly.)
The following snippet illustrates a currency dijit for U.S.
dollars that requires a value for the cents to be explicitly
provided after the decimal point via the fractional constraint, which is the only
additional constraint of interest that this dijit provides besides
those that have already been inherited:
<input dojoType="dijit.form.CurrencyTextBox"
constraints="{min:1,max:100,fractional:true}" currency="USD"/>Like NumberTextBox, the
values for this dijit change to vanilla numeric values when
editing begins, and format back to currency values once editing
ends via a blur event.
ComboBox
ComboBox provides a
drop-down list of values much like an HTML SELECT element; however, a ComboBox is based on an ordinary
input element, so if an
acceptable value is not identified by the list of possibilities,
you may opt to type in any value you'd like. ComboBox inherits from ValidationTextBox, so you have the full
gamut of features for validation available to you; some additional
enhancements are that it also provides a filtered list of possible
values based on the prefix you've entered. The list of values can
be a static list that is established a priori
or a dynamic list from a dojo.data store that may be fetched from
a server.
In its simplest manifestation, you might use a ComboBox simply to provide a static list
of common options, with the ability for the user to type in a
custom option. The following code listing illustrates static data
with the auto-complete feature enabled.
<select name="coffee" dojoType="dijit.form.ComboBox" autoComplete="true">
<option>Verona</option>
<option>French Roast</option>
<option>Breakfast Blend</option>
<option selected>Sumatra</option>
<script type="dojo/method" event="onChange" args="newValue">
console.log("value changed to ", newValue);
</script>
</select>Hooking a ComboBox to an
ItemFileReadStore is quite
simple and involves little more than pointing the ComboBox to the data source. For
example, consider a data source that contains coffee roasts and
their descriptions in the following form:
{identifier : "name",
items : [
{name : "Light Cinnamon", description : "Very light brown, dry, tastes
like toasted grain with distinct sour tones, baked, bready"},
{name : "Cinnamon", description : "Light brown and dry, still toasted
grain with distinct sour acidy tones"},
...lots more...
]
}Assume that you'd like to populate the ComboBox with a name field, and when a change occurs,
use the description in some other meaningful way. You might
accomplish this task as shown in Example 13.6, “ComboBox at work”.
Example 13.6. ComboBox at work
<html>
<head>
<title>Pick a coffee roast, any coffee roast</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
djConfig="parseOnLoad:true",
type="text/javascript"
src="http://o.aolcdn.com/dojo/1.1/dojo/dojo.xd.js">
</script>
<script type="text/javascript">
dojo.require("dojo.parser");
dojo.require("dojo.data.ItemFileReadStore");
dojo.require("dijit.form.ComboBox");
dojo.require("dijit.form.Button");
dojo.require("dijit.form.Form");
</script>
<head>
<body class="tundra">
<div dojoType="dojo.data.ItemFileReadStore"
jsId="coffeeStore" url="./coffee.json"></div>
<form action="localhost" dojoType="dijit.form.Form">
<select name="coffee" dojoType="dijit.form.ComboBox"
store="coffeeStore" searchAttr="name">
<script type="dojo/method" event="onChange" args="newValue">
console.log("value changed to ", newValue);
var f = function(item) {
console.log("new description is ",
coffeeStore.getValue(item, "description")
);
};
coffeeStore.fetchItemByIdentity(
{identity : newValue, onItem : f}
);
</script>
</select>
<button dojoTyype="dijit.form.Button">Submit</button>
</form>
</body>
</html>To recap, all that takes place is that you hook up the
ComboBox to the ItemFileReadStore via the store
attribute, and tell the ComboBox which field to display via the
searchAttr attribute. Then,
when a change occurs, the ComboBox 's onChange method detects and uses the new
value to look up the description from the store.
Tip
Internally, the ComboBox only implements a specialized
subset of the dojo.data.Read/Notification API that
is necessary for it to work. Specifically, it implements the
following methods:
getValueisItemLoadedfetchclosegetLabelgetIdentityfetchItemByIdentityfetchSelectedItem
For completeness, the specific attributes shown in Table 13.9, “ComboBox attributes” are also available for ComboBox.
Table 13.9. ComboBox attributes
FilteringSelect
A FilteringSelect is an
enhanced version of the ordinary HTML select element in that provides a drop-down
list of mandatory values and submits the hidden values and the
displayed values. While FilteringSelect looks like and shares a lot
of features with ComboBox,
including the ability to filter a drop-down list as text is typed and
the ability to fetch data from a serve via a store, it is built upon
an HTML SELECT element.
Three particularly important distinctions between a FilteringSelect and a ComboBox are worth noting:
ComboBoxis built on an ordinaryselectelement in which the value that is submitted to the server on asubmitevent is the control's hidden value, not the visible value in the control. This distinction is an important feature becauseFilteringSelectcan be degradable and behave as much like an ordinarySELECTas possible.The
FilteringSelectinherits fromMappedTextBox(a serializableTextBox) instead ofValidationTextBoxbecause validation is a nonissue because users cannot type free text into the control.FilteringSelectcan display HTML as its label, not just text. Thus, you can include customizable markup such as images in labels.
In addition to common dijit.form operations such as getValue, setValue, getDisplayedValue, setDisplayedValue, and the various ComboBox options, FilteringSelect provides two additional
attributes and one additional function, listed in Table 13.10, “FilteringSelect additions”.
Table 13.10. FilteringSelect additions
|
Name |
Comment |
|---|---|
|
|
The text to display in
the control. If no value is specified, then |
|
|
Whether to treat the
text label as markup or ordinary text. Valid values include
|
|
|
The event handler that is called when the label changes; returns the label that should be displayed. |
MultiSelect
MultiSelect is a simple
wrapper (with the attributes listed in Table 13.11, “MultiSelect”) around a native SELECT element (with the attribute multi=true ) that inherits from _FormWidget. The primary reason that it is
included in Dijit is because it facilitates interaction with the
dijit.Form wrapper (coming up later
in this chapter) and streamlines the task of otherwise having to style
the SELECT element
yourself.
Table 13.11. MultiSelect
|
Name |
Comment |
|---|---|
|
|
The number of elements
to display on a page. |
|
|
Moves the selected
nodes from another |
|
|
Returns the selected nodes in the widget. |
|
|
Sets the value of each
node in the widget according to the sequential values provided
in the values |
|
|
Inverts the selection.
If |
Because MultiSelect is just a
lightweight wrapper around the HTML equivalent, there is little to say
about that is specific to Dojo. You can define a MultiSelect in markup, as shown in Example 13.7, “Typical MultiSelect in markup”.
Example 13.7. Typical MultiSelect in markup
<select multiple="true" name="foo" dojoType="dijit.form.MultiSelect" style="height:100px; width:100px; border:3px solid black;"> <option value="TN" selected="true">Tennessee</option> <option value="VA">Virginia</option> <option value="WV">West Virginia</option> <option value="OH">Ohio</option> </select>
Textarea Variations
A bane of traditional web development has often been the dreaded
TEXTAREA element that takes up a
fixed amount of space on the screen, requiring somewhat of a black art
to determine just how much space to allocate to it so as to maximize
the amount of viewable area while minimizing wastage on valuable
screen real estate.
Textarea
The Textarea dijit inherits
from _FormWidget and gives the
best of both worlds in that it supports the standard HTML attributes
for an ordinary textarea, yet its
appearance is a fixed width element that grows vertically as needed.
The API for the Textarea dijit
simple in that you'll normally only need to use the standard
setValue and getValue methods. onChange is a valuable extension point
that you can use as a callback when a change occurs:
<textarea dojoType="dijit.form.Textarea" style="width:300px">
One fish, two fish...
</textarea>SimpleTextarea
Although Textarea 's
ability to expand is convenient in a lot of cases, it doesn't lend
itself well to situations in which an enclosing container (such as
the layout dijits you'll learn about in Chapter 14, Layout Widgets) needs to dictate its overall size. For
this reason, the SimpleTextarea
dijit was introduced. For all practical purposes, the SimpleTextarea behaves just like an
ordinary TEXTAREA element except
that it can expand and contract in size. You populate it with the
same attributes as an ordinary TEXTAREA element such as rows and cols and, like Textarea, you can use setValue and getValue to manipulate the text in
it.
Button Variations
Dijit provides drop-in, degradable replacements for standard
push buttons and checkboxes, yet it also gives you a lot of
sophisticated options, such as the kinds of buttons that you normally
find in toolbars. Let's start out with an ordinary Button and work our way up through more
sophisticated options.
Button
Figure 13.4, “A typical Button” shows a button, and Table 13.12, “Button properties” gives the rundown on the most basic
kind of button dijit, a Button,
which inherits _FormWidget.
Table 13.12. Button properties
|
Name |
Comment |
|---|---|
|
|
Used to provide the label for the button in markup or via programmatic creation. |
|
|
A Boolean value
designating whether to display the text label in the
|
|
|
A class specifying an image that can make a button appear like an icon. |
|
|
An extension point that is called in response to a click. This is a very common method to override. |
|
|
A method accepting an
HTML string that can change a |
Tip
Unlike TextBox and its
descendants, the Button widgets
require you to use the setAttribute('value', /*...*/) function,
inherited from _FormWidget, to
set value because Buttons don't have a widget
value so much as they have a form value that is relayed
to the server.
Let's dust off the code from Example 13.4, “Updated form to use ValidationTextBox” and provide some
final polish by replacing those ugly buttons, as shown in Example 13.8, “Typical Button usage”. Remembering to add an obligatory
dojo.require("dojo.form.Button")
to the head of the page, the replacement is straightforward. Note
how convenient providing the onClick handler in markup is for this
situation.
Example 13.8. Typical Button usage
<button dojoTye="dijit.form.Button" type="submit">Sign Up!
<script type="dojo/method" event="onClick" args="evt">
alert("You just messed up...but it's too late now! Mwahahaha");
</script>
</button>
<button dojoTye="dijit.form.Button" type="reset">Reset</button>The Button 's iconClass is especially snazzy in that it
doesn't just replace the entire button with an icon. Instead, it
embeds the icon into the button alongside an optional label if one
is specified and showLabel is
true. For example, if you had a
small 20 × 20px thumbnail image of some spam that you wanted to
embed into the "Sign Up!" button, you could do it by including
iconClass="spamIcon" in the
button tag and ensuring that the following class appeared in your
page:
.spamIcon {
background-image:url('spam.gif');
background-repeat:no-repeat;
height:20px;
width:20px;
}Of course, you can provide any customized styles you'd like for buttons to make them look any way that you'd like by applying an inline style or a custom class.
ToggleButton
Because form dijits leverage inheritance so heavily, they
often have common ancestors that provide common functionality for
descendant classes. ToggleButton
is one such class; it inherits from Button and adds in functionality for a
button that has an on/off state, like a RadioButton or a CheckBox. The only notable attribute it
adds is checked, which can be
toggled with setAttribute.
Although you would probably use a more conventional control
like CheckBox to designate on/off
states, you could choose to use ToggleButton directly, or subclass it and
implement your own custom ToggleButton. The onChange extension point (common to all
form dijits) is one particularly useful feature:
<button dojoType="dijit.form.ToggleButton">
<script type="dojo/method" event="onChange" args="newValue">
console.log(newValue);
</script>
</button>Most of the buttons that appear in a toolbar such formatting a
text with italics, bold, underline, etc., use the ToggleButton. The Menu and MenuItem dijits are introduced in Chapter 15, Application Widgets.
Warning
Several button dijits are not included in their own
designated resource file. In particular, you should dojo.require("dijit.form.Button") for
Button, ToggleButton, DropDownButton, and ComboButton. While it may seem odd to
require one thing when you actually want another, the rationale is
that the (inheritance-driven) implementations for the various
buttons are so similar that they are included in the same physical
file to minimize overhead in acquiring resources from the server.
Additionally, recall that the mapping between classes simulated
via dojo.declare and resource
files is not designed to be a one-to-one mapping (although
traditional object-oriented programming philosophy often deems it
so).
This technique remains a source of consternation amongst Dojo circles, as the overhead from a synchronous request to the server would be a moot point in a production setting that uses the facilities from Util to optimize layers for each page of an application.
These kinds of nuances result from so many (well-intentioned) competing interests in the Dojo community.
CheckBox
CheckBox descends directly
from ToggleButton and is a
standard drop-in replacement for an ordinary <input type="checkbox"> element.
Using it is as simple as requiring it into the page and then using
the dojoType tag. We might
introduce it into Example 13.4, “Updated form to use ValidationTextBox” page by disabling
the "Sign Up!" button until after user click the CheckBox to confirm that they're aware of
our covert intentions to spam them:
<divname="confirmation" dojoType="dijit.form.CheckBox"> <script type="dojo/method" event="onClick" args="evt"> if (this.checked) dijit.byId("signup").setAttribute('disabled', false); else dijit.byId("signup").setAttribute('disabled', true); </script> </div> I understand that you intend to spam me.<br> <button id="signup" disabled dojoType="dijit.form.Button" type="submit"> Sign Up! </button>
Figure 13.5, “A series of CheckBox dijits” shows a series
of CheckBox dijits.
Warning
The reason that DIV tags
are being used instead of INPUT
tags is because you cannot embed SCRIPT tags inside of INPUT tags, and if you try, it is almost
a certainty that the browser will strip them out. Thus, if you
want to use SCRIPT markup
inside of dijits, you should be especially cognizant that you
can't use INPUT tags. If
degradability is so important that this isn't acceptable for your
application, simply write the methods in pure JavaScript instead
of markup.
Thus, to programmatically check the CheckBox, you might use the setValue(true) method, which would check
the box as well as set its checked attribute to true and its value attribute to true.
Tip
If it is really important to ensure every page is as
degradable as possible, you can go the extra mile to explicitly
include ordinary HTML attributes in tags. For example, instead of
just specifying <input
dojoType="dijit.form.CheckBox"/>, you could also
include the extra type
attribute, resulting in <input
dojoType="dijit.form.CheckBox
type="checkbox"/>.
Like ordinary HTML checkbox
elements, however, there is a difference in the
state of the checkbox versus the
value of the checkbox. The state of the
checkbox is either that it is or is not checked, and you can detect
the state via the standard checked attribute. The value attribute, however, may take on
non-Boolean values to pass special values to the server if the box
is checked when the form is submitted. For example, a tag like
<input name="pleaseSpamMe"
value="yes"/> would append pleaseSpamMe=yes to the query string if
the form was submitted via GET.
(The default for value is
"on".)
The confusion comes in, however, when you find out that the
getValue method and the value attribute do not always return the
same thing. The way it works is that getValue returns whether the box is
checked regardless of what the actual value attribute happens to be. The
rationale for this design is that the most common use case for a
getValue function would be to
determine a visible on/off state—not getting the actual value, which
may not reflect the on/off state.
Because it is possible to get yourself tangled up in the
differences between some of the different possibilities, consider
some of the common cases for a CheckBox dijit:
<input id="foo" dojoType="dijit.form.CheckBox"></input>
Example 13.9, “Typical CheckBox usage” shows a series of calls to manipulate the dijit along with extensive commenting to show the effects.
Example 13.9. Typical CheckBox usage
/* Check the initial state */
dijit.byId("foo").checked // false
dijit.byId("foo").getValue( ) // "on"
/* Use setValue with true */
dijit.byId("foo").setValue(true) // check the box and set the value to true
dijit.byId("foo").checked // true
dijit.byId("foo").getValue( ) // true
/* Use setValue with false */
dijit.byId("foo").setValue(false) //uncheck the box and set the value to false
dijit.byId("foo").checked // false
dijit.byId("foo").getValue( ) // false
/* Use setValue with a String */
dijit.byId("foo").setValue("bar") //check the box and set the value to "bar"
dijit.byId("foo").checked //true
dijit.byId("foo").getValue( ) // "bar"These most common use cases for the CheckBox are using getValue and setValue with Boolean values as
parameters, so the chances are reasonably good that you won't need
to wade through the potentially esoteric effects that can arise when
you start mixing state and values.
Warning
Here's one particularly unintuitive combination that accentuates some of the issues involved in mixing state and value that you should be especially aware of:
dijit.byId("foo").setAttribute("value", "foo")
// changes the value attribute but does not check the box
dijit.byId("foo").value // "foo"
dijit.byId("foo").getValue( )
//false, because the box is not checkedThe unintuitive part is that after setting a value you
wouldn't expect a call to getValue(
) to return false
because common idioms in JavaScript involve the ability to test a
string value, and if it's not "", null, or undefined, then it evaluates as true. However, the thing to remember is
that getValue( ) consistently
returns whether the box is checked or not—regardless of what is
actual value attribute is set
to be. In this case, the box is not checked, so getValue( ) returns false.
Likewise, the dijit's onChange event will not fire for a
dijit.byId("foo").setAttribute("value",
"foo") method call since the checked state of the box
did not visibly change.
RadioButton
A RadioButton is a drop-in
replacement for the ordinary HTML equivalent descending from
CheckBox, and like its HTML
equivalent, is conceptually a group of checkboxes in which only one
can be selected at any given time. Recall that each button in a
radio group has the same value for name but distinct values for value. Figure 13.6, “A RadioButton group” shows a RadioButton group.
We might even further refine our working example (Example 13.4, “Updated form to use ValidationTextBox”) by asking users
how many times a day they'd like us to bother them. We could use
radio buttons as shown in Example 13.10, “Typical RadioButton usage” to achieve this purpose quite
easily, having first required dijit.form.CheckBox in the
page.
Warning
Although you'd think that last sentence was a typo, it's
not. Recalling that you dojo.require resources, not individual
widgets, it turns out that the dijit.form.CheckBox resource provides
dijit.formCheckBox and dijit.form.RadioButton.
This warning is along the same lines as the previous warning
about how the dijit.form.Button
resource provides multiple dijit implementations.
Example 13.10. Typical RadioButton usage
<input name="spamFrequency" value="1 per day" dojoType="dijit.form.RadioButton"> 1 per day<br> <input name="spamFrequency" value="2 per day" dojoType="dijit.form.RadioButton"> 2 per day<br> <input name="spamFrequency" value="3+ per day" dojoType="dijit.form.RadioButton"> 3+ per day<br>
DropDownButton
A DropDownButton is simply
a descendant of Button that when
clicked produces a drop-down menu with options you can select—just
like you're used to seeing in a toolbar. DropDownButton and dijit.Menu are closely related, in that a
Menu is one of the most common
vehicles for supplying a drop-down list; TooltipDialog is another common option.
Figure 13.7, “A DropDownButton dijit” shows the DropDownButton dijit.
More complete coverage is given to Menu (and the individual MenuItem s it contains) in Chapter 15, Application Widgets. Example 13.11, “Typical DropDownButton usage”, however, demonstrates a
DropDownButton in action and
should get the point across. Note that the first child of the parent
DropDownButton node is a label
that appears on the button.
Example 13.11. Typical DropDownButton usage
<button dojoType="dijit.form.DropDownButton">
<span>Save...</span>
<div dojoType="dijit.Menu">
<div dojoType="dijit.MenuItem" label="Save">
<script type="dojo/method" event="onClick" args="evt">
console.log("you clicked", this.label);
</script>
</div>
<div dojoType="dijit.MenuItem" label="Save as...">
<script type="dojo/method" event="onClick" args="evt">
console.log("you clicked", this.label);
</script>
</div>
<div dojoType="dijit.MenuItem" label="Save to FTP...">
<script type="dojo/method" event="onClick" args="evt">
console.log("you clicked", this.label);
</script>
</div>
</div>
</button>If you want to use a DropDownButton as part of a form
submission, you could do so by creating a hidden INPUT element and programmatically setting
its value via the constituent MenuItem 's onClick method. The most common uses for
DropDownButton, however, normally
involve an application-level behavior, such as saving a
document.
Tip
In general, form fields that are submitted to a server via a
form submission should be visible to the user at the time of
submission. In that regard, DropDownButton may seem a bit misplaced
with its inclusion into dijit.form because it isn't
that kind of form control. The reason it
appears in this section is that it is a descendant of Button, and it would make even less
sense to try to have a Button
ancestor living in another namespace.
ComboButton
A ComboButton inherits from
DropDownButton, but with a twist:
it provides a reserved area that produces a drop-down when it is
clicked, whereas if you click on the "other" part of the button that
is initially visible, it invokes a default action. For example, you
might have a "Save" button that triggers an ordinary save action
when clicked, while clicking the drop-down portion of the button
produces a menu with options such as "Save", "Save as . . . ", "Save
to FTP site", and so on. Figure 13.8, “Left: a ComboButton before clicking on the expander; right:
the ComboButton after clicking on the expander” shows a
ComboButton before and after
clicking on the expander.
Figure 13.8. Left: a ComboButton before clicking on the expander; right: the ComboButton after clicking on the expander
Example 13.12, “Typical ComboButton usage” illustrates using
a ComboButton.
Example 13.12. Typical ComboButton usage
<button dojoType="dijit.form.ComboButton">
<span>Save</span>
<script type="dojo/method" event="onClick" args="evt">
console.log("you clicked the button itself");
</script>
<div name="foo" dojoType="dijit.Menu">
<div dojoType="dijit.MenuItem" label="Save">
<script type="dojo/method" event="onClick" args="evt">
console.log("you clicked", this.label);
</script>
</div>
<div dojoType="dijit.MenuItem" label="Save As...">
<script type="dojo/method" event="onClick" args="evt">
console.log("you clicked", this.label);
</script>
</div>
<div dojoType="dijit.MenuItem" label="Save to FTP...">
<script type="dojo/method" event="onClick" args="evt">
console.log("you clicked", this.label);
</script>
</div>
</div>
</button>Notice that the label for the ComboButton is still provided via the
first child element, <span>Save</span> in this
case, and the options that are provided via the drop-down are just
the same as with a DropDownButton.
Slider
While a slider may not be a native HTML form control, there can be little dispute about how useful sliders can be for highly visual interfaces. Whether your goal is to adjust the transparency for an image, adjust the amount of a particular color in a custom color combination, or resize some other control on the screen, a slider can help you do it in a very intuitive manner. Dijit offers both horizontal and vertical sliders.
Tip
The Slider dijit is an
especially slick piece of engineering. Like some of the other
dijits, it keeps track of the current value via a hidden form value
so that when you submit a form, the value is passed over to the
server just like any other form field.
To get all of the various Slider machinery into your page, simply do a
dojo.require("dijit.form.Slider").
In addition to VerticalSlider and
HorizontalSlider, you also get the
supporting classes for rules and labels. Let's start with something
simple and gradually add some complexity so that you get a better feel
for exactly how customizable this fantastic little widget really
is.
HorizontalSlider
Suppose that as a caffeine junkie, you want to create a
horizontal slider that denotes caffeine levels for various
beverages. Your first stab at getting a plain vanilla slider into
the page might be something like the Example 13.13, “HorizontalSlider (Take 1)”, remembering to
first require dijit.form.Slider
into the page.
Example 13.13. HorizontalSlider (Take 1)
<div dojoType="dijit.form.HorizontalSlider" name="caffeine"
value="100"
maximum="175"
minimum="2"
style="margin: 5px;width:300px; height: 20px;">
<script type="dojo/method" event="onChange" args="newValue">
console.log(newValue);
</script>
</div>To summarize, the code created a slider without any kinds of
labels whatsoever; the slider displays values ranging from 2 through
175 with the dimensions provided by the inline style. The default
value is 100, and whenever a change occurs, the onChange method picks it up and displays
it to the console. Note that clicking on the slider causes its value
to move to the click point. So far, so good.
To further refine the slider, let's remove the buttons that
are on each end of it by adding showButtons="false" as an attribute and
adding a HorizontalRule and some
HorizontalRuleLabels to the top
of the slider. Everything you need was already slurped into the
page, so no additional resources are required; we pull in the
dojo.number module, however, to
facilitate formatting to the console.
Just add some more markup into the body of the existing slider, as shown in Example 13.14, “HorizontalSlider (Take 2)”.
Example 13.14. HorizontalSlider (Take 2)
<div dojoType="dijit.form.HorizontalSlider" name="caffeine"
value="100"
maximum="175"
minimum="2"
showButtons="false"
style="margin: 5px;width:300px; height: 20px;">
<script type="dojo/method" event="onChange" args="newValue">
console.log(dojo.number.format(newValue,{places:1,pattern:'#mg'}));
</script>
<ol dojoType="dijit.form.HorizontalRuleLabels" container="topDecoration"
style="height:10px;font-size:75%;color:gray;" count="6">
</ol>
<div dojoType="dijit.form.HorizontalRule" container="topDecoration"
count=6 style="height:5px;">
</div>
</div>Presto! The slider is already looking much sharper with the
addition of some ticks to break up the space and some percentage
labels. Note that it is not necessary to have a one-to-one
correspondence between the rules and the rule labels, but in this
case, it works out quite nicely. Additionally, the attribute
container used an enumerated value, topDecoration, defined by the slider to
place the rules and labels.
Although the slider contains a percentage rating, it would be
nice to bring in some domain specific data for the bottom of the
slider. The basic pattern is the same as before, except that we'll
use the slider's bottomContainer
instead of its topContainer.
However, instead of relying on the dijit to produce some bland
numeric values, we provide the contents of the list ourselves in
Example 13.15, “HorizontalSlider (Take 3)”, including
explicit <br> tags in
multiword beverages to keep the display looking sharp. Figure 13.9, “A HorizontalSlider” shows the result.
Example 13.15. HorizontalSlider (Take 3)
<div dojoType="dijit.form.HorizontalSlider" name="caffeine"
value="100"
maximum="175"
minimum="2"
showButtons="false"
style="margin: 5px;width:300px; height: 20px;">
<script type="dojo/method" event="onChange" args="newValue">
console.log(newValue);
</script>
<ol dojoType="dijit.form.HorizontalRuleLabels" container="topDecoration"
style="height:10px;font-size:75%;color:gray;" count="6">
</ol>
<div dojoType="dijit.form.HorizontalRule" container="topDecoration"
count=6 style="height:5px;">
</div>
<div dojoType="dijit.form.HorizontalRule" container="bottomDecoration"
count=5 style="height:5px;">
</div>
<ol dojoType="dijit.form.HorizontalRuleLabels" container="bottomDecoration"
style="height:10px;font-size:75%;color:gray;">
<li>green<br>tea</li>
<li>coffee</li>
<li>red<br>bull</li>
</ol>
</div>VerticalSlider
VerticalSlider works just
like HorizontalSlider except that
it renders along the y -axis, and you'll use
leftDecoration and rightDecoration instead of topDecoration and bottomDecoration to specify container
values for the rules and rule labels, as well as adjust your style
to space elements out horizontally instead of vertically. Example 13.16, “VerticalSlider” is the same slider, but adjusted for
the vertical axis. Figure 13.10, “A VerticalSlider” depicts the
result.
Example 13.16. VerticalSlider
<div dojoType="dijit.form.VerticalSlider" name="caffeine"
value="100"
maximum="175"
minimum="2"
showButtons="false"
style="margin: 5px;width:75px; height: 300px;">
<script type="dojo/method" event="onChange" args="newValue">
console.log(newValue);
</script>
<ol dojoType="dijit.form.VerticalRuleLabels" container="leftDecoration"
style="height:300px;width:25px;font-size:75%;color:gray;" count="6">
</ol>
<div dojoType="dijit.form.VerticalRule" container="leftDecoration"
count=6 style="height:300px;width:5px;">
</div>
<div dojoType="dijit.form.VerticalRule" container="rightDecoration"
count=5 style="height:300px;width:5px;">
</div>
<ol dojoType="dijit.form.VerticalRuleLabels" container="rightDecoration"
style="height:300px;width:25px;font-size:75%;color:gray;">
<li>green tea</li>
<li>coffee</li>
<li>red bull</li>
</ol>
</div>Table 13.13, “Horizontal Slider and VerticalSlider”, Table 13.14, “HorizontalRule and VerticalRule”, and Table 13.15, “HorizontalRuleLabel and VerticalRuleLabel” illustrate the important facets
of the dijit.form.Slider ;
namely, the sliders themselves, the rules, and the labels. Remember
that all of the ordinary form machinery, such as setValue, et al., is inherited and works
as usual.
Table 13.13. Horizontal Slider and VerticalSlider
|
Name |
Type |
Comment |
|---|---|---|
|
|
Boolean |
Whether to show
increment/decrement buttons on each end of the slider.
|
|
|
Integer |
The minimum value
allowed. |
|
|
Integer |
The maximum value
allowed. |
|
|
Integer |
The number of
discrete value between the minimum and maximum (inclusive).
|
|
|
Integer |
The amount of
adjustment to nudge the slider via the page up and page down
keys. |
|
|
Boolean |
Whether clicking the
progress bar causes the value to change to the clicked
location. |
|
|
Number |
The time in
milliseconds to take to slide the handle from 0% to 100%.
Useful for programmatically changing slider values. |
|
|
Function |
Increments the slider by one unit. |
|
|
Function |
Decrements the slider by one unit. |
Table 13.14. HorizontalRule and VerticalRule
|
Name |
Type (default) |
Comment |
|---|---|---|
|
|
String |
The CSS class to apply to individual hash marks. |
|
|
Integer |
The number of hash marks to generate. 3 by default. |
|
|
DOM Node |
Where to apply the
label in relation to the slider: |
Tip
HorizontalRuleLabel and
VerticalRuleLabel inherit from
HorizontalRule and VerticalRule, respectively.
Table 13.15. HorizontalRuleLabel and VerticalRuleLabel
|
Name |
Type (default) |
Comment |
|---|---|---|
|
|
String |
The CSS class to apply to text labels. |
|
|
Array |
Array of text labels
to render, evenly spaced from left-to-right or
top-to-bottom. |
|
|
Integer |
The number of numeric
labels that should be omitted from display on each end of
the slider. (Useful for omitting obvious start and end
values such as |
|
|
Integer |
When the labels array
is not specified, this value provides the leftmost label to
include as a label. |
|
|
Integer |
When the labels array
is not specified, this value provides the right-most label
to include. |
|
|
Object |
The pattern to use
(from |
|
|
Function |
Returns the |
Form
Although form dijits can be wrapped in an HTML form tag, the dijit.form.Form dijit provides some
additional conveniences that are quite useful. This section rounds off
the chapter by reviewing ordinary HTML forms and then reviews the
specific features provided by dijit.form.Form. A common source of
confusion to many Dijit newcomers is that they expect Dijit to do
something directly that already works just fine via ordinary HTML.
Recall that a significant part of Dojo's design philosophy is to not
reinvent aspects of web technologies that already work; rather, Dojo
supplements and augments as needed where web technology is lacking or
not standardized.
HTML Form Tag Synopsis
dijit.form.Form respects
the standard form attributes as
defined in the HTML 4.01 specification. All attribute values are
assumed to be wrapped in quotes as string values, although DOM
events such as onclick entail
explicitly denoting that a script action is expected, like onclick="javascript:someScriptAction( )"
or onclick="javascript:return
someValidationAction( )". For mouse events, a "left-click"
action is assumed.
Form
The Form dijit itself
supplements the standard HTML form attribute by providing several
methods that may be called to manipulate it directly, and one
extension point that is called internally in response to a user
action. Table 13.16, “Form methods and extension points” lists
the key aspects of Form.
Table 13.16. Form methods and extension points
|
Name |
Category |
Comment |
|---|---|---|
|
|
Method |
Returns a JSON structure providing named key/value pairs for the form. |
|
|
Method |
Returns |
|
Method |
Provides a concise way of setting all values in the form at one time via a JSON structure where each key in the structure is a named form field. |
|
|
Method |
Used to programmatically submit the form. |
|
|
Method |
Systematically calls
|
|
|
Extension point |
Called internally
when the |
|
|
Method |
Returns |
Wrapping up the entire form into a dijit.form.Form is just like replacing any
other element with the corresponding dijit, as shown in Example 13.17, “Typical Form usage”.
Example 13.17. Typical Form usage
<form id="registration_form" dojoType="dijit.form.Form">
<!-- form elements go here -->
<!-- override extension points as usual...-->
<script type="dojo/method" event="onSubmit" args="evt">
//return false if form should not be submitted. By default
//onSubmit returns isValid( ) for the dijit.form.Form
</script>
</form>Summary
This chapter has covered some serious ground. After working through it, you should:
Understand how ordinary HTML forms work
Understand how to use drop-in form dijit replacements for standard form elements
Be familiar with the general taxonomy of form dijits, understanding the broad strokes of the inheritance relationships
Be able to create form dijits both programmatically and in markup
Understand the difference between methods, attributes, and extension points
Understand what is meant by a degradable form and be able to weigh the various factors involved in producing a degradable design
It's time to move on to layout widgets.
[24] Mac OS X Firefox 2.0+ users may need to download the Configuration Mania add-on at https://addons.mozilla.org/en-US/firefox/addon/4420 to enable tabbing into buttons.















Add a comment



Add a comment