This excerpt is Chapter 3 from Learning WML & WMLScript, published in October 2000 by O'Reilly.
Tasks and Variables
The <prev> Task
The <refresh> Task
A WML task is an element that specifies an action to be performed by the browser, rather than something to be displayed. For example, the action of changing to a new card is represented by a
<go> task element, and the action of returning to the previous card visited is represented by a
<prev> task element. Task elements encapsulate all the information required to perform the action.
Tasks are used in many places in WML. Events (discussed later in this chapter) are tied closely with tasks, and many of the user interface elements (see Chapter 4, WML User Interaction) use tasks to perform actions.
To see how tasks are used in context, consider the element
<do>, which represents some sort of control that the user can activate, such as a softkey, a menu item on a cell phone, or maybe even an onscreen button if the device has a touchscreen. A
<do> element isn't itself a task element. Rather, it contains a task subelement that specifies the action to perform when the user activates the control.
<do> element that, on activation, simply assigns the value
wibble to the variable
test can be written as:
<do type="accept"> <refresh> <setvar name="test" value="wibble"/> </refresh> </do>
To have the same
<do> element instead send the browser to a card called
card2 in the current deck, you could write:
<do type="accept"> <go href="#card2"/> </do>
Note that the
<do> element is exactly the same for these two examples. The only difference is the task element (the
<refresh> or the
<go>). This added consistency (or orthogonality to use the technical term) is an important benefit of using tasks.
<do> element is explained more fully in Chapter 4.
All tasks can change variables in the browser's context using the
<setvar> element, as described in Chapter 2, WML Variables and Contexts. The new variable bindings don't affect the task itself but rather take effect when the task completes. For example, suppose the variable
page contains the value
login. The task:
<go href="$(page).wml"> <setvar name="page" value="bad"/> </go>
bad.wml, because the browser substitutes the original value of the
page variable into the
href attribute before it assigns the new value to the variable.
As the name suggests, the
<go> task represents the action of going to a new card. (It is also used for a special purpose with WMLScript, but you must wait until later in the book to find out about that.)
<go> task takes several different attributes to customize exactly how to find the new card. Usually, only
href and sometimes
method attributes are used.
href(required variable url)
method(optional string; default
post, corresponding to the GET and POST methods of HTTP.
sendreferer(optional boolean; default
true, the browser sends the URL of the current deck along with the request. This URL is sent as a relative URL if possible. The purpose of this is to allow servers to perform simple access control on decks, based on which decks are linking to them. For example, using HTTP, this attribute is sent in the HTTP
accept-charset(optional string; default
unknown(the default), the browser uses the same character set that sent this document to the browser. (Note that this attribute is an advanced feature and is rarely used.)
One of the more interesting options available on the
<go> task is the
method attribute. This specifies whether the request should be sent as a GET or as a POST. This option is used only when sending information for processing on the server: it's not used when simply fetching static pages of WML.
If you know HTML, you may recognize similarities with the
METHOD="POST" attributes that can be put on an HTML
<FORM> element. WML puts this attribute on the
<go> task instead, but it has essentially the same effect.
GET is the normal method used in HTTP. All the information sent to the server is encoded in the URL, and the server uses this URL to find some resource and return it to the browser.
The main advantage of GET is that it's simple. Any information is simply added to the query part of the URL (more on the parts of URLs in Appendix A, Absolute and Relative URLs). You can even put the query information directly into the URL using variables.
The main disadvantage of GET is that it can be used only for a limited amount of data. Web servers and other programs that process URLs impose certain limits on the length of a URL they are prepared to handle. This limits the size of the request that can be sent using GET.
A subtler problem with GET relates to the fact that all the information you send becomes part of the URL. Many browsers display the URL of the current deck somewhere on the screen (even if only briefly), and most web servers store the entire URL in their log files, complete with the extra data from the GET. If this information is of a sensitive nature (a password, for example), it's displayed on the screen and then saved for posterity in the web server's logs!
The POST method avoids these two problems, by sending the data separately from the URL in the request. As a result, the URL stays small, and the browser display and web server logs don't contain any of the data.
In modern versions of WML, information to be posted with the POST method is specified in
<postfield> elements within the
<go> element. This information takes the form of a list of name/value pairs. Each
<postfield> element specifies a single pair. The element is very simple, having only two attributes:
name(required variable string)
value(required variable string)
<postfield> elements to be used even with the GET method. In this case, the fields are added to the end of the query part of the URL. For example, consider the task:
<go href="wibble" method="get"> <postfield name="x" value="17"/> <postfield name="y" value="42"/> </go>
This has the same effect as:
<go href="wibble?x=17&y=42" method="get"/>
<postfield> element in this way can make your WML much clearer and also makes your life much easier if you have to change the WML to use POST at some point in the future.
You can even mix the two styles. Here's another way to write exactly the same
<go> task as the last two examples:
<go href="wibble?x=17" method="get"> <postfield name="y" value="42"/> </go>
One form of task is more common than any other: a
<go> task that has no attributes other than
href and doesn't contain any
<setvar> elements. Because this form is so common, WML provides a shorthand form you can use in many situations.
Instead of including the task as a complete
<go> element, the value that would be put into the
href attribute of the
<go> element is simply included as an attribute on a different element. For example, it is possible to bind a task to an option in a selection list, so that the task is performed when the option is selected. The normal way of doing this looks like this:
<option> <onevent type="onpick"> <go href="foo.wml"/> </onevent> Option text <option>
Using the shorthand form, this can be written as:
<option onpick="foo.wml"> Option text </option>
I think you'll agree, that's much shorter and clearer.
This is allowed for the
ontimer attributes of the
<card> element; the
onpick attribute of the
<option> element; and the
href attribute of the
<a> element. These elements are all described later in this book: don't worry about them for now.
<prev> task represents the action of returning to the previously visited card on the history stack. When this action is performed, the top entry is removed from the history stack, and that card is displayed again, after any
<setvar> variable assignments in the
<prev> task have taken effect.
<prev> task takes no attributes.
Most uses of the
<prev> task are very simple. Usually no variables are involved, and so most
<prev> tasks are simply:
<prev> task is most commonly used in connection with a
<do> element (described in Chapter 4). Some browsers don't provide a back button unless one is specified in the WML, and so a great deal of WML contains the construct:
This simply provides a button or some other user-interface construct, which when activated, sends the browser to the previous card.
One situation where it can be useful to include variables in a
<prev> task is a login page, which prompts for a username and password. In some situations, you may want to clear out the password field when returning to the login card, forcing the user to reenter it. This can be done with a construct such as:
<prev> <setvar name="password" value=""/> </prev>
<refresh> task is the simplest task that actually does something. Its effect is simply to perform the variable assignments specified by its
<setvar> elements, then redisplay the current card with the new values. The
<prev> tasks perform the same action just before displaying the new card.
<refresh> task doesn't take any attributes.
<refresh> task is most often used to perform some sort of "reset" action on the card. Example 3-1 shows how this could be done. The
<input> elements prompt the user to enter strings and then store the strings into the variables specified in their
name attributes (see Chapter 4 for more information). There is also a
<go> task using the POST method to submit the login and password information to a server for processing.
Example 3-1: A Reset Button
<?xml version="1.0"?> <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml"> <wml> <card title="Reset button example"> <!-- Read login and password from user. --> <p>Login: <input name="login"/></p> <p>Password: <input name="password"/></p> <!-- Submit button sends data to server. --> <do type="accept" label="Submit"> <go href="login.cgi" method="post"> <postfield name="l" value="$(login)"/> <postfield name="p" value="$(password)"/> </go> </do> <!-- Reset button clears login and password. --> <do type="reset" label="Reset"> <refresh> <setvar name="login" value=""/> <setvar name="password" value=""/> </refresh> </do> </card> </wml>
The purpose of the
<noop> task is to do nothing (no operation). This may seem particularly useless: why would anyone want a task that doesn't do anything?
The only real use for this task is in connection with templates (discussed in more detail in Chapter 6, WML Decks, Templates, and Cards). However, it can also be specified anywhere a task is required.
<noop> task is the only exception to the rule that tasks can set variables. It can't set variables and can't even contain any
<setvar> elements. If you want a task to just set variables and not change the current card, use the
<noop> task doesn't take any attributes.
<noop> task takes no attributes and can't even contain any
<setvar> elements, it always looks the same in use:
An event in WML is simply something that can happen to some element from time to time. For example, entering a
<card> element triggers an event on the
<card>, and selecting an
<option> from a selection list triggers an event on the
You can harness these events by binding them to a task. The usual way of doing this is with the
<onevent> element. As mentioned earlier in this chapter, for simple
<go> tasks you can usually make use of a simpler form: this will be mentioned when when we discuss the elements in question.
For example, the
<option> element (detailed in Chapter 4) declares an item in a list of selections. When this item is selected, it triggers an
onpick event on the
<option> element. Suppose the element were declared without an event handler, like this:
<option> Purple </option>
In this case, the
onpick event is ignored, since there is no handler. If, on the other hand, the option is declared as:
<option> <onevent type="onpick"> <go href="#purple"/> </onevent> Purple </option>
onpick event is handled by executing the
<go> task, sending the browser to a new card.
<onevent> element declares an event binding. It can't contain anything except a single task element that is performed when the event occurs. It may be present inside either an
<option> element (see Chapter 4) or a
<card> element (see Chapter 6). In either case, the
<onevent> element (or elements) must be the first elements declared inside their enclosing element.
<onevent> element takes only one attribute:
type="ontimer"to bind to the
ontimerevent of a
Sometimes, you may want to do something special when the user enters a particular card. For example, you may want to initialize the values of variables so that the display looks correct. Another thing you may want to do is to clear out some variables when the user returns to a particular card in the history stack.
To make this possible, WML defines two events,
onenterbackward, which happen on a
<card> element when the user enters it. Which event occurs depends on how the card was entered.
onenterforward event occurs when a card is entered in the forward direction. Entering as a result of a
<go> task, selecting a bookmark, or entering a URL directly are all in the forward direction. The most common use of this event is to initialize things that must be set up before the card is displayed, often by using a
<refresh> task or by using a
<go> task to run some WMLScript (see "Calling WMLScript from WML" in Chapter 13, WMLScript Functions).
Example 3-2 shows how this can be used. When the first card is entered forwards, the
<refresh> task is performed, which initializes the
state variable. This variable can then be updated by other WML pages and is passed through to the server by the
Example 3-2: Initialization on Entry
<?xml version="1.0"?> <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml"> <wml> <card title="onenterforward example"> <!-- Initialize state to zero on first entry. --> <onevent type="onenterforward"> <refresh> <setvar name="state" value="0"/> </refresh> </onevent> <!-- Collect some information from the user. --> <p><input name="text"/></p> <!-- Send the text and the state to the server. --> <do type="accept"> <go href="submit.cgi"> <postfield name="s" value="$(state)"/> <postfield name="t" value="$(text)"/> </go> </do> </card> </wml>
If the task bound to the
onenterforward event is a simple
<go> task without
<postfield> elements, you can use the shorthand form introduced earlier in this chapter: just add an
onenterforward attribute to the
<card> element. The value of this attribute is the destination URL.
For example, the event binding:
<card title="example"> <onevent type="onenterforward"> <go href="#card2"/> <onevent> <!-- rest of card --> </card>
is equivalent to the shorter form:
<card title="example" onenterforward="#card2"> <!-- rest of card --> </card>
It's your choice to use the shorthand form, but it means less typing and results in less data being sent to the browser.
Be warned that not all tasks you can bind to the
onenterforwardevent actually make sense. For example, the event binding:
<onevent type="onenterforward"> <prev/> </onevent>
makes it impossible for the user to enter the card at all: as soon as she went to the card, the browser would immediately return to the previous one!
To make matters worse, the event binding:<card id="card1" onenterforward="#card1">
means that as soon as the browser entered the card, it would be immediately redirected to the same card, which would cause an immediate redirect to the same card, and again, and again. . . . Well-written browsers may notice this and signal an error, but not all browsers are well-written: many simply lock up or even crash.
onenterbackward event is the companion of the
onenterforward event for the backward direction. This event is triggered when the card is returned to as the result of a
<prev> task or some other action that navigates backwards in the history stack.
The most common use for the
onenterbackward event is to reset state back to some initial value when returning to the beginning. Example 3-3 alters Example 3-1 to illustrate this: instead of an explicit reset button, the login and password are cleared when the user returns to this card via the history stack.
Example 3-3: Reset on Reentry
<?xml version="1.0"?> <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml"> <wml> <card title="Reset on reentry example"> <!-- Reset fields when entered backwards. --> <onevent type="onenterbackward"> <refresh> <setvar name="login" value=""/> <setvar name="password" value=""/> </refresh> </onevent> <!-- Read login and password from user. --> <p>Login: <input name="login"/></p> <p>Password: <input name="password"/></p> <!-- Submit button sends data to server. --> <do type="accept" label="Submit"> <go href="login.cgi" method="post"> <postfield name="l" value="$(login)"/> <postfield name="p" value="$(password)"/> </go> </do> </card> </wml>
Just as with the
onenterbackward also has a shorthand form in the case where the task is a simple form of
<go>, meaning that the event binding:
<card title="example"> <onevent type="onenterbackward"> <go href="foo.wml"/> </onevent> <!-- rest of card --> </card>
is equivalent to:
<card title="example" onenterbackward="foo.wml"> <!-- rest of card --> </card>
Martin Frost is the author of Learning WML & WMLScript
Return to the Wireless DevCenter.
Copyright © 2009 O'Reilly Media, Inc.