Summer’s been fairly brutal throughout much of both the US and Canada the last few weeks, and transplanted American that I am I have to admit that I’m beginning to appreciate what 35°C really means and that it’s a lot warmer than one might expect - a sweltering (for Victoria) 95° F. As Victoria is on an island that usually has most extremes of temperature moderated by sea breezes and is positioned on the largely leeward side of the island, it says altogether too much about the wave of heat that has had most of the continent in thrall … and yes, I say this knowing full well that I’m about to receive all kinds of brickbats from those of you who’ve been toughing it out in 110° F (or a mind-boggling 43°C) temps that have hit much of the plains.

I gave a keynote address on SVG last week at the GeoWeb conference in Vancouver, and will be sharing my remarks from that shortly. Today, I’d like to give you the fifth installment about XForms, of six - this has been a remarkably well read series, which gives me a certain amount of hope for the specification. I should be making an announcement shortly concerning XForms in this space …

Trigger Happy

In the last column, you may have noticed that there was a singular lack of discussion about one of the most basic of all components - the button. Now, this may seem like a particular striking omission (attributable perhaps to writing these columns way too late at night) but in fact there was a bit of method in madness.

One of the most striking things about a button is the fact that, unlike a slide, input box, select box, or output field, a button does not in general reflect state (I’ll deal with the special use case of toggle buttons in a bit). Instead, when you press a button, in general what you are doing is initiating an action - you are changing the state in the rest of the application, in essence.

This notion informed the terminology of XForms: unlike in traditional HTML you have no <xf:button> element. Instead, you have the <xf:trigger> element, which “triggers” a particular action to occur. Such a trigger is similarly conceived of as being an abstract element, though most XForms implementations (including Mozilla Firefox) represent such a trigger as a button.

In HTML, the event which you use to pick up the press of a button is almost invariably the click event, (usually throught the onclick event handler). However, XForms makes some fundamental assumptions about how you will interact with the environment, and one of the most critical is that you should never assume that the only mechanism (or even an acceptable mechanism) in use is a mouse. Hitting an ENTER key, speaking a command word, perhaps even using a psionic confabulator in order to telepathically indicating that the action should occur are all considered viable (if somewhat outre) ways of interacting with the form, and consequently you should get away from the mouse-centric viewpoint of the onclick handler.

In its place you get the highly intuitive DOMActivate event (okay, their hearts were in the right place, anyway). This event basically says that whenever you click on the control, it will automatically “activate” and perform the indicated action. Oh well.

So what, you ask, is this particular indicated action? Or more properly, the question is - so when you press the button, where does the particular script block go? This is when the XForms gurus look at you with a curious smile on their face and say “Please, young grasshopper … what is this ’script block’ that you are talking about?” or perhaps “Oh, foolish grasshoppers, you place to much emphasis on the doing, not the being.” or even “What is the sound of one glorp clapping?” … but only if they’ve been sniffing some really interesting incense.

In point of fact, the primary actions supported by XForms are declarative, written in XML as a block contained within an <xf:action> element within the trigger. For instance, consider a simple counter - pressing a button increments a field (xformsEvent1.xhtml):

<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:xf="http://www.w3.org/2002/xforms" 
    xmlns:ev="http://www.w3.org/2001/xml-events">
    <head>
        <title/>
        <xf:model>
            <xf:instance id="dataStore">
                <data counter="1"/>
            </xf:instance>
        </xf:model>
    </head>
    <body>
        <xf:trigger>
            <xf:label>+</xf:label>
            <xf:action ev:event="DOMActivate">
                <xf:setvalue ref="@counter" 
                      value="(instance('dataStore')/@counter)+1"/>
            </xf:action>
        </xf:trigger>
        <xf:output ref="@counter"/>
    </body>
</html>            
            

Once again, the XForms approach differs considerable from the normal HTML approach. In the HTML approach, the control displaying the content would also contain the content. However here its the model that holds the value (an attribute named counter) and the control only acts as a means to display this value.

To the trigger itself - the trigger starts out with a label that contains the contents to be displayed, which in this case will be displayed on the button face itself. Below that is the <xf:action> element. This is in essence simple a grouping device, and is not strictly speaking in the case of a single action to be performed. What is important here is that either the action or the control itself should contain the name of the event which “trips” the trigger - here the rather oddly named “DOMActivate” element. The events themselves are in a separate namespace xmlns:ev="http://www.w3.org/2001/xml-events", reflecting the use of the W3C’s xml-events. Note that as with most namespaces the use of the “ev” prefix isn’t required - you could just as readily call it event: or evt: (or snorg: for that matter) but it has become conventional.

The actual action is contained in this case in the <xf:setvalue> statement. This particular action first establishes a node context (here ref="@counter", which determines which node in the model will receive the result of the operation. The value attribute, in turn is an XPath expression that evaluates to either a scalar value or an XML node. In this case, the calculation is simple - retrieve the value already in the @counter attribute and add one to it. When the action is invoked, the increment occurs, and the model then notifies all of the form components that have links to @counter that they should update (in this case the following <xf:output> should reflect this change.

Again the XForms approach is very classically MVC - you are not updating the output element directly. Instead, the output element only reflects the changes made into the model itself. This design pattern has a number of names, but is perhaps closest to the broadcaster/observer pattern where changes that are made to a data source automatically cause any objects with dependencies upon (interests in) those changes to be notified, and hence to update themselves.

This notation is a little unwieldy, and there are simplifications possible, but the notation also makes possible some interesting effects. For instance, suppose that instead of a plus sign, you wanted two buttons - a happy face button indicating that an increment should occur, and a sad face button indicating that a decrement action should occur. The label in this particular case (and in fact in general) can take any HTML content, including text formatting and images, and place it on the button face. Thus, the new counter can be revised from the old as follows ( (xformsEvent2.xhtml):

 <html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:xf="http://www.w3.org/2002/xforms" 
    xmlns:ev="http://www.w3.org/2001/xml-events">
    <head>
        <title/>
        <xf:model>
            <xf:instance id="dataStore">
                <data counter="1"/>
            </xf:instance>
        </xf:model>
    </head>
    <body>
        <xf:trigger>
            <xf:label><img src="imagesbu/2.gif"/><br/><b>Decr</b></xf:label>
            <xf:setvalue ref="@counter" value=". - 1"  ev:event="DOMActivate"/>
        </xf:trigger>
        <xf:output ref="@counter"/>
        <xf:trigger>
            <xf:label><img src="imagesbu/1.gif"/><br/><b>Incr</b></xf:label>
            <xf:setvalue ref="@counter" value=". + 1" ev:event="DOMActivate"/>
        </xf:trigger>
    </body>
</html>
            

The first thing to notice in this situation is the use of smiley (or sad) faces and text within the label, as discussed above. This content can be pretty much anything that can be expressed within HTML or SVG. The button will resize itself to fit the content within it, which means that you could theoretically create buttons that hold whole web pages or screen-sized images and they would display appropriately.

Perhaps more intriguing here is the <xf:setvalue> statements here. Because there is only one such statement, the <xf:action> grouping statement is not necessary. However, some way of tracking the event is necessary. Thus, the event has been moved directly onto the <xf:setvalue> statement itself.

Additionally, the complex instance() statement is gone, replaces with “. - 1″ and “. + 1″ respectively. The XForms parser assumes that it is likely that the action being performed on a given node in the model will relate in some way to that node, so the value statement evaluates in the context defined by ref. Since the context is already the @counter attribute, specifying “@counter -1″ would in fact try to find the counter attribute of the counter attribute. Instead, you can use the current node operator “.” to indicate the current context node. While it can make such XPath expressions a little less clear, it usually can make them more concise.

Getting the Message

Suppose that you wanted a message to pop up when you clicked on a particular button - an indicator of why the various happy/sad faces are in those states, for instance. XForms can help you considerably there. For instance, the <xf:message> element can be placed within an action block and will thus be automatically invoked whenever the button is pressed (xformsEvent3.xhtml):

<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:xf="http://www.w3.org/2002/xforms" 
    xmlns:ev="http://www.w3.org/2001/xml-events">
    <head>
        <title/>
        <xf:model>
            <xf:instance id="dataStore">
                <data counter="1"/>
            </xf:instance>
        </xf:model>
    </head>
    <body>
        <xf:trigger>
            <xf:label><img src="imagesbu/2.gif"/><br/><b>Decr</b></xf:label>
            <xf:action ev:event="DOMActivate">
                <xf:setvalue ref="@counter" value=".-1"/>
                <xf:message level="modal">You are giving the Glorp less food 
            (food = <xf:output ref="@counter"/>) and that makes it sad.</xf:message>
            </xf:action>
        </xf:trigger>
        <xf:output ref="@counter"/>
        <xf:trigger>
            <xf:label><img src="imagesbu/1.gif"/><br/><b>Incr</b></xf:label>
            <xf:action ev:event="DOMActivate">
                <xf:setvalue ref="@counter" value=".+1"/>
                <xf:message level="ephemeral"><div style="width:200px;">
            <img src="imagesbu/1.gif"/>You are giving the <b>Glorp</b> more food  
            (food = <xf:output ref="@counter"/>) and that is making it happy.</div>
               </xf:message>
            </xf:action>
        </xf:trigger>
    </body>
</html>            
            

Here, the value of <xf:action> becomes more obvious - the message object is a form of action (and normally it would take an ev:event attribute, but because it’s now within the scope of the <xf:action> object, the event handler is assumed. When the DOMActivate event is invoked, a message will pop up giving some basic information. These messages are halfway between tooltips and formal dialogs and come in three flavors:

  • modal. The (more or less) standard alert box, which can display limited text markup but not images. This stays until it’s dismissed, and no other actions can occur while it is up.
  • modeless. Similar to a modal dialog in form, but you can interact with the calling page. This is very useful for help content, as you can both see and copy and paste correctly formatted content from the dialog.
  • ephemeral. This is something like a tooltip - it stays up for a limited period of time or until clicked, then disappears. The power of the ephemeral dialog, however, is that you can place rich content (including pictures) into it, making it a potentially useful vehicle for creating context menus and other floating content.

The message dialogs are a direct response to the blocking actions of the normal JavaScript alert() command, which is often used by developers to report status messages even at the cost of pausing the program. By making dialogs modeless, you can alert your users to a problem without forcing them to respond while the ephemeral message works even better at providing temporary status messages that go away spontaneously when ignored.

Pay close attention to the fact that you can in fact use <xf:output> (and other controls) within these dialogs. This means that rather than hard coding messages, the messages can be generated in such a way that they contain more detailed information about the particular problem or issue involved. Similarly, though not shown, you can use the <xf:message> statement with a ref attribute to pull content directly from the data model.

Valid Arguments

Shifting to setvalue nodes for just a second, it’s worth noting here that you can constrain the actions of these nodes through the use of calculate bindings. For instance, you couldn’t provide less than zero food pellets to your poor glorp, so you need to have some realistic way of assuming that you cannot go negative on the decrements. Similarly, you may have only so many food pellets available to give at any given time, so there may be an upper bound to the values as well.

You can use the XForms if() statement to check to insure that you are in fact neither going below or above the requisite boundaries. These could be placed in the setvalue commands, such as:

<setvalue ref="@counter" value="if(. &lt; 1,0,. - 1)"/>

in which case this will automatically reset the value to 0 if you attempt to drop below that.

On the other hand, given that bind statements automatically apply any time a referenced object gets changed, you could also pass the constraints directly into the binding, as follows (xformsEvent4.xhtml):

<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:xf="http://www.w3.org/2002/xforms" 
xmlns:ev="http://www.w3.org/2001/xml-events">
<head>
    <title/>
        <xf:model>
            <xf:instance id="dataStore">
                <data counter="1" minval="0" maxval="5"/>
            </xf:instance>
            <xf:bind nodeset="@counter" calculate = 
        "if(.&lt;= ../@minval,../@minval,if(.&gt;= ../@maxval,../@maxval, .))"/>            
        </xf:model>
        </head>
        <body>
            <xf:trigger>
                <xf:label><img src="imagesbu/2.gif"/><br/><b>Decr</b></xf:label>
                <xf:action ev:event="DOMActivate">
                    <xf:setvalue ref="@counter" value=". - 1"/>
                </xf:action>
            </xf:trigger>
            <xf:output ref="@counter">
            </xf:output>        
            <xf:trigger>
                <xf:label><img src="imagesbu/1.gif"/><br/><b>Incr</b></xf:label>
                <xf:action ev:event="DOMActivate">
                    <xf:setvalue ref="@counter" value=". + 1"/>
                </xf:action>
            </xf:trigger>
        </body>
</html>            
            

However, ideally, you should be able to take advantage of the fact that XForms can internally determine when it’s reached an invalid state and correct itself. This is where the xforms-invalid event comes in. This is a form-level event (rather than one associated with a given component) and is fired whenever the form ceases to be valid. An example of how such a form could work is shown in xformsEvent5.xhtml

<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:xf="http://www.w3.org/2002/xforms" 
xmlns:ev="http://www.w3.org/2001/xml-events">
<head>
<title/>
<style type="text/css">
@namespace xf url("http://www.w3.org/2002/xforms");
xf|output:invalid .xf-value {color:red;}            
    </style>
    <xf:model>
        <xf:instance id="dataStore">
            <data counter="1" minval="0" maxval="5" />
        </xf:instance>
        <xf:bind nodeset="@counter"
        constraint="(. &gt;= ../@minval) and (. &lt;= ../@maxval)">
        </xf:bind>
    </xf:model>
    </head>
    <body>
        <xf:action ev:event="xforms-invalid">
            <xf:message level="modal"><xf:output 
                value="if(@counter &lt; @minval,
                'You cannot have a negative number of pellets',
                if(@counter &gt; @maxval, 
                concat('You cannot have more than ',@maxval,' pellets.'),''
                )
                )"/>
            </xf:message>
            <xf:setvalue ref="@counter" 
                    value="if (. &lt;= ../@minval,../@minval,.)"/>
            <xf:setvalue ref="@counter" 
                    value="if (. &gt;= ../@maxval,../@maxval,.)"/>
        </xf:action>
        <xf:trigger>
            <xf:label><img src="imagesbu/2.gif"/><br/><b>Decr</b></xf:label>
            <xf:action ev:event="DOMActivate">
                <xf:setvalue ref="@counter" value=". - 1"/>
            </xf:action>
        </xf:trigger>
        <xf:output ref="@counter">
        </xf:output>        
        <xf:trigger>
            <xf:label><img src="imagesbu/1.gif"/><br/><b>Incr</b></xf:label>
            <xf:action ev:event="DOMActivate">
                <xf:setvalue ref="@counter" value=". + 1"/>
            </xf:action>
        </xf:trigger>
    </body>
</html>            
            

The challenge with the XForms environment comes in dealing with a potentially large number of factors that can make a form invalid - in essence, unlike with procedural programming, the ability to catch exceptions in XForms 1.0 is somewhat limited, though this is addressed to a greater degree in the XForms 1.1 specification.

Of Links and Scripts

It’s easy to lose sight of the fact, when discussing XForms, that such forms are intended to be used within the context of web content. Indeed, the ability to link - to replace web page content with new content or to spawn a new web window, is actually a very well supported action under XForms. This action is performed using the <xf:load> element.

This element, like the <xf:setvalue> command, can work well with both static links and ones directly (or derived from) the data model. The static case is handled with the resource attribute, which contains a static link:

<xf:trigger>
    <xf:label>XForms.org</xf:label>
    <xf:action ev:event="DOMActivate">
        <xf:load resource="http://www.xforms.org" show="replace"/>
    </xf:action>
</xf:trigger>            
            

In this particular case, clicking on the trigger (the button) will cause www.xforms.org to load in place of the current web page. This fragment will render as a button with the label XForms.org on it. However, interestingly enough, if you set the trigger’s appearance attribute to "minimal", the button will be replaced with an inline hypertext link, using the label as the text displayed. In essence,

<p>Visit <xf:trigger appearance="minimal">
    <xf:label>XForms.org</xf:label>
    <xf:action ev:event="DOMActivate">
        <xf:load resource="http://www.xforms.org" show="replace"/>
    </xf:action>
     </xf:trigger>!
</p>

is the same as

<p>Visit <a href="http://www.xforms.org">XForms.org</a>!</p>

raising the question about why you would need to go to such extremes to create a link in XForms. For the purely static case, the concern is probably merited.

However, the situation changes when you start dealing with dynamic links. The <xf:load> element can take all the same single node binding properties that <xf:output> and others can take, including ref and bind. As an example, the following takes a link from the data model (xformsEvent7.xhtml):

<html xmlns="http://www.w3.org/1999/xhtml" 
     xmlns:xf="http://www.w3.org/2002/xforms"
    xmlns:ev="http://www.w3.org/2001/xml-events">
    <head>
        <xf:model>
            <xf:instance>
                <data src="http://www.xforms.org" title="XForms.org"/>
            </xf:instance>
        </xf:model>
    </head>
    <body>
        <p>Visit <xf:trigger appearance="minimal">
            <xf:label ref="@title"/>
            <xf:load ref="@src" show="new" ev:event="DOMActivate"/>
        </xf:trigger></p>
    </body>
</html>            
        

After the page is loaded, when the user clicks on “XForms.org” the link to that site is activated and launches a new window (show="new") displaying that site.

You can also take advantage of the load command to launch simple JavaScript commands by using the javascript: protocol.

<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:xf="http://www.w3.org/2002/xforms"
    xmlns:xbl="http://www.mozilla.org/xbl"
    xmlns:ev="http://www.w3.org/2001/xml-events">
    <head>
        <title/>
        <style type="text/css">
@namespace xf url("http://www.w3.org/2002/xforms");
            </style>
            <script type="text/javascript">
var notify= function(){alert('You just called the notify function.');}            
        </script>
            <xf:model>
                <xf:instance id="dataStore">
                    <data url="javascript:notify()"/>
                </xf:instance>
            </xf:model>
            </head>
            <body>
                <p> 
                    <xf:trigger>
                        <xf:label>Invoke Javascript call.</xf:label>
                        <xf:load ref="@url" ev:event="DOMActivate"/>
                    </xf:trigger>
                </p>
            </body>
</html>
        

What happens here is that the load command reads the @url reference to retrieve the string 'javascript:notify()'. The javascript: protocol takes the text after the javascript: command and evaluates it in the global JavaScript context. This means that if other functions are defined in the page the evaluation will call that function. Unfortunately, this does not pass an event object as part of the call, which significantly limits the types of things that can be done with this approach, but it is good for generalized scripts, especially when they come in as part of a data model.

The next logical jump from here is get into scripts that are aware of the objects invoking them, and that can in fact operate on the XML models themselves. In general the best way of calling these is to go back, once again, into the XBL binding layer, a discussion which I’ll defer to the next XForms column.


Kurt Cagle is an author, web architect and consultant specializing in XML, AJAX and Web 2.0 services. He lives in Victoria, BC and had learnt the fact that while -40° C = -40° F, 40° C most certainly does not equal 40° F