Programming with formsPlayer

This handbook contains everything you need to know to build rich internet applications using formsPlayer and XForms. Since XForms is a W3C standard then nearly all of the ideas and code samples in this handbook can be used with other XForms processors.

Setting Up Your Development Environment

Building an XForms application might be as simple as editing a file with Notepad and pressing F5 in the browser, or as complex as configuring servers and pipelines, and building an XForm on the fly. Either way you will need some kind of editor and some kind of XForms processor.

Get an Editor

Since XForms are XML documents then any text editor can be used to create and edit them. If you've done much HTML development you may already have a favourite editor, in which case you are already up and running.

If you decide to do much serious development with XForms then you will probably find it easier to make use of an XML editor. Such editors will help keep your document structured correctly, and some editors have features that are specific to XForms. For example, XFormation will help you with your XPath expressions, whilst both Eclipse and XFormation can save a lot of time by automatically generating forms from schemas, WSDL files and XML data documents.

We'll discuss some of these editors in a separate tutorial, since to get started with this tutorial, you need nothing more than your current HTML editor, or simply use Notepad or Wordpad.

Get an XForms Processor

There are a large number of XForms processors, and which one is most appropriate for you will depend on a number of factors. Issues such as which devices and platforms you are targetting, and whether the XForms should be translated into something else or delivered to the end-user intact will all need to be considered.

The easiest development configuration is to install a client-side processor onto your development machine; even if the forms are later deployed in some other fashion it makes for an extremely quick design and development cycle if you develop them using a client-side XForms processor, running inside a browser, without the need for a server.

For a quick way to get going with this tutorial use formsPlayer, a plug-in for Internet Explorer 6 on Windows. You can get an automatic install (using a signed CAB file) from the project page, which also contains links to the support forums, screenshots, samples, lists of bugs and feature requests, and so on.

Creating a Form Template

You'll find it easier to create new forms if you have an empty template that you can start with. Many XML editors will allow you to place such a template in a special folder so that it is available as a choice whenever you create a new XML document.

The template that follows is an HTML document that contain XForms; even if you plan to use XForms in some other language--and XForms was designed to make that possible--we would still recommend beginners to get acquainted with XForms within HTML.

To create the template:

  1. Create a new, empty file using your preferred editor;
  2. Paste in the following mark-up:
    <?xml version="1.0" encoding="utf-8"?>
    <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"
     xmlns:xs="http://www.w3.org/2001/XMLSchema"
    >
      <head>
        <script src="http://lib-xh.googlecode.com/svn/trunk/xH.js" type="text/javascript">/**/</script>
        <title>Empty XForm</title>
      </head>
      <body>
        <p>Empty XForm</p>
      </body>
    </html>
  3. save the file to a handy location or to your XML editor's templates directory, calling it something like xforms-template.html.

As you can see the document is much the same as an ordinary HTML page, with the following differences:

  • The XForms, XML Events and XML Schema namespaces have been added to the root element:
    <?xml version="1.0" encoding="utf-8"?>
    <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"
     xmlns:xs="http://www.w3.org/2001/XMLSchema"
    >
      <head>
    

    We don't always need XML Events and XML Schema, but since this is meant to be a template that you can just use without thinking about it, we might as well add them here.

  • there is a reference to the lib-xh loader script:
        <script src="http://lib-xh.googlecode.com/svn/trunk/xH.js" type="text/javascript">/**/</script>
    

    This script checks for XForms support on the browser that your users have used to load your form. If formsPlayer is present, that is used, and similarly if the Firefox XForms extension is available, that will be used. The script will also automatically install formsPlayer if the user is running Internet Explorer, and no version of formsPlayer is present.

Testing Your Development Environment

Now you are ready to start building web applications with XForms! Let's see if we can create a working form, before we move on to the tutorial.

  1. Create a new form by using your template.
  2. In the body element, replace the text Empty form with:
    <xf:input ref="q" incremental="true">
      <xf:label>Search:</xf:label>
    </xf:input>
    (<xf:output ref="q" />)
    
  3. Save your new form to a convenient location. If you opened your template as a quick way to get started then be sure to use Save As....
  4. Use IE6 to open the file that you just saved. You should see:
    • the formsPlayer splash-page for a second or two;
    • a single input control with a caption of Search;
    • a pair of brackets.

    Anything you now type into the input control should also be displayed between the brackets:

    Screenshot of form that tests that the development environment is working

If this does not happen then there may be a problem with your installation or browser security settings. For information on troubleshooting see the installation forum.

Introduction to XForms

A key idea of our approach to building internet applications is making good use of standards-based languages, in particular XForms.

XForms is an exciting new language from the W3C that can be used to create anything from simple forms to complex Web 2.0 applications. XForms are dynamic, cross-platform, accessible, script-free...and 100% standard.

This handbook covers everything you need to know to get started with XForms. You'll start with a simple walkthrough to get your development environment up and running, then create two fully functioning applications--one that saves links to del.icio.us, and another that searches Flickr. Both use CSS-driven Ajax animations.

Once you've seen what XForms can do, building web applications will never be the same!

Handbook reviewed 2006-12-06, by MB.

What is XForms?

XForms is a relatively recent standard from the W3C, designed to allow us to create sophisticated user interfaces using mark-up. This means that defining a user interface is much the same as using HTML, except that XForms has been designed from the ground up to cope with many of the things that we usually have to dive into script to do.

And XForms doesn't just make it easy to replace script in our applications, it also provides us with the means to manipulate and validate XML; unlike most languages that you might have used, XForms brings XML right into the heart of the language.

In this section we'll look at some of the things you can do with XForms, but if you want to jump right in and look at some mark-up, feel free to skip to Feet First...Some Basics

Business rules

XForms makes it very easy to create dependencies between different parts of a web application. Of major significance is that these dependencies are established at the level of the data, before being reflected in the user interface. For example, say a form contains a data value for 'number of years at the current address' and another for 'previous address'; a rule could be created that says the latter need not be completed if the form-filler has been at their current address for less than three years.

In XForms this rule is defined completely independently of the actual user interface itself, but the XForms processor will ensure that any form control that is representing the 'previous address' fields will only be made relevant (i.e., available for the user to fill in), once the correct conditions are met.

It's also possible to define a rule that says the 'previous address' field is required if the form-filler has only lived at their current address for a certain amount of time. An XForms processor can prevent the form from being submitted to the server, if this condition is not met.

Since these 'business rules' are defined using a standard mark-up language and XPath statements, they become very easy to develop, maintain, and test. This is because, unlike traditional e-forms systems where such rules are tightly coupled with the user interface, in XForms there is a separate data layer within which the logic sits. (XForms was designed to make use of the Model-View-Controller pattern.)

In the following screenshot from the mortgage application sample (see the showcase area) a user has indicated that the purpose of the mortgage they are applying for is to buy a bungalow:

Screenshot of mortgage application form with purpose of mortgage set to bungalow.

However, when the selection is changed to indicate the purchase of a flat, three further fields automatically become available to the user, to allow the capture of more information:

Screenshot of mortgage application form with purpose of mortgage set to flat.

Data-driven styling

Since data-entry fields can be shown and hidden as other data is entered, it is important to provide some sort of indication to the user that the form has changed--particularly on large forms. formsPlayer makes various highlighting features available to the form author in a simple and concise way.

In the following screenshot we can see two highlighting techniques being employed at the same time; one is a yellow highlight that lasts for 2 seconds, and indicates to the user that some controls that were not previously available are now available, whilst the second technique draws the user's attention to some status information that is shown in green, for about 8 seconds:

Screenshot of mortgage form showing warnings and highlights after selecting 'converted flat'.

Once the highlights have timed out, the form looks like this:

Screenshot of mortgage form showing 'converted flat' selection.

XForms allows us to define both the conditions for the highlighting and the highlighting itself in simple mark-up, and it is easy to add other effects--for example, a red background when a control becomes invalid.

Routing

Routing is a similar concept to relevance. However, XForms allows quite complex routing between form controls. For example, on a large form with many pages, it is often the case that to fix an error in one page the user needs to navigate back to a previous page. The HTML-like mark-up employed by XForms allows these types of relationships to easily be established.

In the following screenshot two form controls are in effect linked together since for them both to be valid, the length of time remaining on a lease cannot be greater than the full term of the lease:

Screenshot of the lease condition controls on the mortgage form.

However, if there is an error the user may not choose to correct the nearest control since the fault may be with the other part of the 'pair'. For example, if the full term of the lease was set to 60, and the remaining term set to 65, it's difficult to which of the two values is in error. To make things easier for the user, the form includes 'shortcuts' to the 'other' control; in the error messages below (only shown when the data is invalid) the text with the light blue background refers to the 'matching' control, and clicking on it causes the focus to be set to that form control:

Screenshot of the alert messages on the lease condition controls.

In this example the controls are close by, but in a very large form this technique can greatly assist the user.

Tooltips and help

XForms provides extremely powerful mechanisms for providing help to users, both in the form of tooltips as well as [F1]-style help. Importantly, XForms has evolved to correct many of the limitations of current forms technologies, and allows for fully internationalised and accessible hints. In the following screenshot an XForms hint is used to show an image as well as formatted text:

Screenshot of a hint with an image in.

Hints can also be multimodal--formsPlayer supports delivering hints and other messages using voice technology:

Screenshot of the Amazon search side-bar, with a voice hint.

One other point worth drawing attention to is that XForms provides a mechanism for items such as hints and help (as well as error messages and form control labels) to be provided by external XML files, making it extremely easy to first define the structure and logic of a form, and then to 'drop in' any number of alternative files. This allows the same form to be provided to users who speak different languages.

Data submission

XForms allows for many different types of data submission, providing an enormous degree of flexibility when talking to back-end systems. Data can be submitted in formats ranging from pure XML, to XML with embedded data (such as images or Word documents), through to the simple serialisation of name/value pairs. (This latter feature allows XForms to communicate with server-side systems that were designed to be used with HTML forms.)

In addition to this wide range of possible data formats, XForms also supports a variety of protocols. This means that XForms not only allows for data to be transferred using HTTP, but can also cope with the saving and loading of local files, as well as transferring data via email and even saving to local ODBC databases.

Platform independence

XForms has been designed to be platform independent, and processors for different devices are emerging. For example, organisations such as IBM are working on the use of voice with XForms, allowing the same form to be used unchanged on both a desktop PC and a touch-tone phone system.

x-port have a number of initiatives underway in this area, ranging from the user of voice prompts in forms, through to the development of Pocket PC and Mac versions of formsPlayer.

Feet First...Some Basics

As always when learning a new language it's best to jump straight in and see the language in action, and leave learning the detailed rules of grammar until a little later. So in the following sections we'll build up a working form using a variety of features from the XForms lanuage, and we'll leave it to a later tutorial to explain the language itself. Any code in the following sections assumes that you've started with the template discussed earlier.

The Input Control

The simplest way to get data from a user is much the same as in HTML forms--by using the input control. However, although the controls have the same name, they do have a slightly different structure. We'll see an example first, and then look at how the mark-up differs from what you might be used to in HTML. The control we'll examine asks a user for a URL:

<xf:input ref="url">
  <xf:label>URL:</xf:label>
</xf:input>

The first difference from HTML that we can see, is that we must provide a label for the control. This shows how seriously accessibility has been taken in the design of XForms, and means it is now possible for voice systems to really know which text on a page applies to which control, rather than having to guess, as screenreaders are forced to do with today's forms.

The second change is that the data to be entered by the user is referred to using the ref attribute, rather than the name attribute.

The final change is that the name of the control has to have a qualifier on it, to show that it comes from the XForms language, and not the HTML language. This is pretty common when using a combination of XML languages, and distinguishing between languages is easily achieved by using a namespace prefix.

So far, so good. Now lets create another control--this time one that asks for a description for this URL:

<xf:input ref="description">
  <xf:label>Description:</xf:label>
</xf:input>

Hopefully no surprises there, since the only differences between this and our URL control are the text of the label, and the value used in the ref attribute.

If you want to follow along, copy the mark-up for the two controls into the body element in a document based on your template, and then view the document in Internet Explorer. As you can probably imagine, a form with just these two controls doesn't do much more than allow you to type in a URL and a description of it:

Screenshot of a simple form with two input controls

Adding Style

Before we go too much further, it's probably quite important for the more artistic amongst you to learn how to style our controls. The good news is that since XForms uses XML mark-up, then you can use everything you have learned about styling HTML documents.

Using CSS

To show how CSS is used, we'll restyle our form so that each control is on its own line, with all the data entry areas lining up vertically. We'll also add a nice dotted line to separate each control. The result will be something like this:

Screenshot of a form with styled controls

The CSS rules for this are pretty simple, so I'll show them first and then explain what it all means. To update your form, place the following in the head element, after the title, and then refresh the browser:

<style type="text/css">
  html
  {
    font-family      : "Trebuchet MS", Verdana, Helvetica, Sans-Serif;
    font-size        : x-small;
  }

  xf\:input,
  xf\:output
  {
    width            : 100%;
    padding-top      : 0.4em;
    padding-bottom   : 0.1em;
    border-bottom    : #d0c49d 1px dashed;
  }

  xf\:label
  {
    width            : 12em;
    vertical-align   : middle;
    margin-right     : 0.2em;
  }
</style>

The first part--the rules for the html element--will no doubt be familiar to you, and simply set a default font for our document.

The next set of CSS rules concern all input and output controls; to indicate that we want these rules to apply to the XForms input and output elements rather than elements with the same name from a different language (you never know!), we include the colon in the CSS selector. Note that since CSS uses the colon to specify pseudo-classes, we must escape it by placing a '\' in front:

  xf\:input,
  xf\:output
  {
    .
    .
    .

The first rule for input and output controls sets the width of the control to 100%, ensuring that each control takes up a whole line. Similarly the first rule for labels sets their width to 12em, which has the effect of lining up all the data entry areas.

Anatomy of a Control

Your first thought when looking at the previous rules may have been that setting the width of an input to 100% ought to have made just the data entry area take up an entire line, on a separate line from the label. This is a common assumption for people used to using HTML, and the reason is that the HTML input control is only a data entry area, with any labels that might apply to it being separate and taking up their own space. This means that there is no way to style the label for a specific control.

In XForms things are different. The control encloses its own label and the data entry area, like this:

Diagram showing the various parts of a control

Each of these parts can be styled independently, and since they have a common parent they can also be styled in relation to each other. For example, if we want the data entry area of all input controls to have a light green background, we would use this rule:

  .input-value,
  {
    background-color : lightgreen;
  }

The resulting form would look something like this:

Screenshot of input controls with light green backgrounds for the data entry part

Experiment

Try altering the values of the three parts of a control--the container, the label and the value--and see what happens. For example, see if you can make all labels bold, and place the label and data entry parts of controls on separate lines.

Tooltips

A common requirement in modern web applications is to show tooltips or mouseover hints to users to help them fill in a form more accurately. Simple tooltips using plain text can be created in HTML using the title attribute (available on images and links). But this approach doesn't allow us to use further mark-up, perhaps to show images or make use of internationalised text.

Some Ajax libraries go some way to addressing this problem, by placing the tooltip into a div element, and then using event handlers and JavaScript to make it visible at the right time. The problem with this is that although functionality is improved in comparison to HTML, the result is forms that are non-portable and non-accessible: they are non-portable, because not only are they dependent on script, but they are dependent on a particular script library; they are non-accessible because a voice browser would have no idea what to do with the div.

XForms provides powerful tooltip features by learning from both the HTML and the Ajax library techniques. XForms uses nice clean mark-up that hides any implementation details, just like the HTML title approach, but it uses an element rather than an attribute so that we can include other mark-up. The element is called hint, and can be used on any form control.

The Hint Element

Let's add some hints to our form to help our users, starting with a simple one on the URL control:

<xf:input ref="url">
  <xf:label>URL:</xf:label>
  <xf:hint>Please enter the URL for the link you want to store</xf:hint>
</xf:input>

If you add this hint to the form so far, you should end up with something like this (when you've opened the form, move your mouse over the URL control to see the hint):

Screenshot showing a simple hint

Next we'll add a hint to the description control, but this time we'll make use of the HTML strong element to emphasise to our users what it is exactly that we want them to enter:

<xf:input ref="description">
  <xf:label>Description:</xf:label>
  <xf:hint>Enter a <strong>description</strong> for the link</xf:hint>
</xf:input>

(Note that you don't need to use a prefix on HTML tags in the way that we are doing for tags from the XForms language.)

The form will now look something like this:

Screenshot showing a simple hint with embedded HTML

You can use pretty much any HTML or XForms elements you like inside hint, making this a very easy way to create complicated user interfaces; for example, when a user hovers over the name of a person or a company, you could show the corresponding face or logo. Similarly, hovering over a product name might show its picture. In fact, you could even use the hint to show a further sub-form that collects more information from the user. Nesting mark-up in this way is a powerful technique which you will see used again and again, and we'll show some real examples in a later tutorial.

Context-sensitive Help

Whilst desktop applications nearly always have some form of help text available to guide the user, web applications generally do not. This is because there is no straightforward mechanism to provide help to a user in HTML; instead the author needs to produce correctly formatted help files, or register for specific keyboard events.

XForms provides the help element to fill this need.

The Help Element

As with hint, the help element can be added to any form control, can contain other mark-up, and hides its implementation details--all of which make it very easy for authors to use.

Let's use the help element to provide more information about the Description control:

<xf:input ref="description">
  <xf:label>Description:</xf:label>
  <xf:hint>Enter a <strong>description</strong> for the link</xf:hint>
  <xf:help>
    Although called a <em>description</em>, del.icio.us uses this
    field more like a title.
  </xf:help>
</xf:input>

Once you've updated your form, reloaded it and put your cursor in the Description field, press F1. The display you have should look something like this:

Screenshot of help text for the 'description' control

The user can continue to interact with the form whilst the help is displayed--it won't be removed until the OK button is pressed. The help window can be moved out of the way if necessary, simply by dragging the blue title bar.

Experiment

Since you can place other mark-up into the help text, try adding the del.icio.us logo, or some links to external documents that might give your users even more information. Remember that in HTML, if you want your link to open in a new window, you need to set the target attribute to _new...otherwise you will lose your application!

HOWTO: Saving bookmarks to del.icio.us

Now that we know how to build a simple form and help the user with their data entry, our next step is to do something useful with their input. We'll now extend our simple two field form to save the data the user has entered--a URL and a description--to the del.icio.us social bookmarking system.

If you're not familiar with del.icio.us then you'll find a link below to a description, along with instructions for obtaining an account that you can use with this tutorial. If you already have an account, then go right ahead and add the submission details.

(If you want to get the completed source code, it's available here.)

Tutorial reviewed 2006-12-06, by MB.

Overview of del.icio.us

If you haven't used del.icio.us before, it's an online link manager that allows you to save links to useful things on the web, along with a description (so you can remember what the link was about) and some tags (to help you group them). The great thing is that unlike the set of favourites that are stored by your browser and will only work with that specific browser, you can get to your del.icio.us links from anywhere.

(For some, the most interesting thing about del.icio.us is that all of your bookmarks are public. In other words, other people can see your links, and you can see theirs--what has been called social bookmarking.)

del.icio.us not only provides an extremely useful service, but it also has a non-browser based interface to that service; this type of arrangement is often called an API, and it means that we can build our own link manager using whatever tools we want, as long as we post the right values to the del.icio.us servers.

Since everyone posts to the same URLs, the del.icio.us API needs some way of telling which store to place the data in. It does this by requiring you to authenticate. This is handled for us by XForms, but if you want to follow on with this example, you'll need to create a del.icio.us account.

Getting a del.icio.us Account

If you don't already have one, you can obtain an account by going to the del.icio.us registration page.

Saving links to del.icio.us

To save links to del.icio.us we need to make an HTTP GET request to the following URL:

https://api.del.icio.us/v1/posts/add

The minimum parameters required are the URL of the link to be added, and a description. For example:

https://api.del.icio.us/v1/posts/add?url=http://skimstone.x-port.net&description=The+skimstone+site.

Other parameters can be added, and they include more detailed notes about the link, a space-separated list of tags, a timestamp, a flag to indicate whether to replace any pre-existing information (if there is any) and a flag to indicate whether to keep the link private.

Details of other commands that can be used with posts are at http://del.icio.us/help/api/posts.

Specifying submission requests

The del.icio.us API supports all sorts of methods for adding, deleting and retrieving your links. The particular method that we are interested in for the moment is add, which allows us to post new links to the store.

Although called an API, the actual interaction we have with del.icio.us is via simple HTTP requests. To add a link to the del.icio.us store for example, we need to send the correct information to the following URL:

http://del.icio.us/api/posts/add

The specific parameters del.icio.us needs from us to store a link, are:

  • url: the URL of the item;
  • description: a description of the item.

XForms will automatically create parameters in the request using the names that we use in the controls, so we made sure earlier to set the names in our input controls to be the same as for the del.icio.us add method:

<xf:input ref="url">
  <xf:label>URL:</xf:label>
</xf:input>
<xf:input ref="description">
  <xf:label>Description:</xf:label>
</xf:input>

All we have to do now is define the end-point for the request--where we want this data sent to. For this we use the XForms submission element, which is part of the XForms model. (The model provides us with a lot of features beyond just submitting data, and we'll see more of these later.)

To set up the request, insert the following model and submission elements into the head element in your form:

    </style>
    <xf:model>
      <xf:submission id="sub-add-link"
	action="http://del.icio.us/api/posts/add" method="get"
      />
    </xf:model>
  </head>

This gives us a request that will send the values in our two controls to del.icio.us, but we still need some way to kick off the submission.

The Submit Control

We created a submission that will send the data the user has entered to del.icio.us, and we gave it an id value of sub-add-link. We can now create a submit button that refers to this named submission, and when the user clicks on it, the data in the form will be sent to del.icio.us, as follows:

    <xf:submit submission="sub-add-link">
      <xf:label>Save</xf:label>
    </xf:submit>
  </body>
</html>

When you reload your form you'll notice that the submit button has been added, but it looks a little odd:

Screenshot of the del.icio.us form...but with a very wide submit button!

This is because the text in the submit button comes from a label just like any other form control, and we defined a CSS rule such that all labels should be 12em wide:

  xf\:label
  {
    width            : 12em;
    vertical-align   : middle;
    margin-right     : 0.2em;
  }

We can easily fix this by changing our CSS rule to be more specific; we'll make it apply only to labels that are children of input controls:

  xf\:input xf\:label
  {
    width            : 12em;
    vertical-align   : middle;
    margin-right     : 0.2em;
  }

Adding images to labels

Let's improve our button a little, and add the del.icio.us logo. Edit the label on the submit element to include an image tag:

    <xf:submit submission="sub-add-link">
      <xf:label>
        <img src="http://del.icio.us/static/img/delicious.gif" alt="" />
        Save
      </xf:label>
    </xf:submit>

Save and refresh, and you should see a much nicer looking button:

Screenshot of the del.icio.us form with the del.icio.us logo on the submit button

We can now move on to test everything.

Testing our Form by Saving a Bookmark

To test the form, get a URL for a document that you want to save for later. You could use a link to this tutorial:

http://www.formsPlayer.com/introduction-to-xforms

Enter this into the URL field, along with a brief description--something like:

An introduction to creating web applications with XForms.

Your form should now look like this, and be ready to send:

Screenshot of the del.icio.us form with data ready for saving.

When you press Save you'll be asked to log in. If you have used del.icio.us before, then you may already be logged in, but if not you will get a prompt similar to this:

Screenshot of the del.icio.us log-in prompt.

Once you've entered your information, you will see that the form is replaced with a simple success message to indicate that your link has been saved:

Screenshot of the del.icio.us success reply.

This is exactly how HTML forms work--the current page is replaced with a new one from the server--and is the default behaviour in XForms. However, we can do far more interesting things with submission, as we'll see later.

In the meantime, if you want to assure yourself that this worked, open your del.icio.us page in a new browser. You can find your page by making a URL from your login name and the del.icio.us home-page. For example, my page is:

http://del.icio.us/mark.birbeck

If you're happy that your link was saved, we can move on to add some more features to the form.

Required Values

Many of the forms that we fill in on the web are part of processes that require very specific data. It may be our credit card details or the time of the return flight, but whatever it is, nearly every form we fill in will have values that are required for some operation to complete successfully.

HTML forms don't allow us to specify which form controls must be completed, so there are generally two approaches that are taken. The first is to let the server check for the values after the user has completed the form, and the second is to add some script to the form to check all the required data is present, and prevent sending the data if it isn't.

Of course any significant process on a server is going to have to check the values anyway, but if we don't check the values in the web browser we will have made an unnecessary round-trip to the server, and provided our users with a poor experience.

However, although the alternative of using script improves the user experience, it ultimately suffers from the same problem that we keep coming back to; it relies on a technique that is non-standard, and available only to programmers.

XForms solves this problem by making this functionality available to mark-up authors via a simple attribute that indicates that some particular piece of data is required. The attribute is called required and it sits in the model, on an element called bind.

We'll illustrate the use of this property by indicating in our form that the URL and Description controls must be filled in (i.e., the items called url and description are required data). Before we show how to add these rules, try pressing Save with either of the two controls empty, and you'll see that del.icio.us objects:

Screenshot of the del.icio.us error message.

So, in order to prevent the submission if either URL or Description are empty, update the XForms model as follows:

    <xf:model>
      <xf:submission id="sub-add-link"
       action="http://del.icio.us/api/posts/add" method="get"
      />
      <xf:bind nodeset="url" required="true()" />
      <xf:bind nodeset="description" required="true()" />
    </xf:model>

Once you have added the above mark-up to your form, save and reload, and then try pressing the Save button without filling any data in. As long as one or other of the URL or Title controls are empty, nothing will happen, and you will never be able to post to del.icio.us.

Unfortunately, now if there is an error, nothing happens. In the next section we'll see how to tell our users that something has gone wrong.

Messages

Using the required property we have now been able to prevent any data being sent to del.icio.us if one or other of the obligatory fields is missing. However, as things stand the user of our form would be none the wiser as to what has gone wrong, and may even just keep pressing the Save button.

In HTML, the usual way to provide feedback to the user is to call the alert() function in script. This doesn't give us very rich messages though, so XForms solves this--in just the same ways as it did with hint and help which we discussed before--by providing the message element.

Let's see what a message that tells the user that they haven't entered enough information for the form to proceed, might look like:

<xf:message level="modal">
  Please ensure that you have entered both a
  URL <em>and</em> a description.
</xf:message>

The level attribute indicates what type of message we want, and in this case we're using a modal message, which halts processing until the user acknowledges it.

But when does this message get displayed to the user? As it stands, never. This is because the message element is one of a number of XForms handlers which will do a specific task, but will only do so when instructed to. However, we know when we want this action to be performed, and that is when there is an error on submission of the data; so to ensure that our message action is carried out we need to register for the notification that will be issued when a submission error occurs.

Registering for Events

It's extremely important in both Ajax and desktop applications to know how the submission of data is progressing. In XForms, information is provided to us both when the submission begins, and again when it ends.

The way in which all notifications happen in XForms is via events. A process will dispatch an event to any listener that registered for it, and this will in turn execute whatever actions have been marked up.

In the example we've been looking at we want to register to be notified if ever the xforms-submit-error event occurs on the submission labelled sub-add-link. The action we want to execute when this event does occur is to show a message. That may all sound very complicated, but in fact the mark-up to do this is as straightforward as adding the message handler as a child of the submission element:

    <xf:submission id="sub-add-link"
       action="http://del.icio.us/api/posts/add" method="get"
    >
      <xf:message level="modal" ev:event="xforms-submit-error">
        Please ensure that you have entered both a
        URL <em>and</em> a title.
      </xf:message>
    </xf:submission>

Making the message action a child of submission ensures that we are registered on the correct element, but we still need to say what event we are registered for, since there are many possible notifications. That's the purpose of the ev:event attribute, which specifies the event we're interested in--in this case xforms-submit-error.

One more thing to complete the mark-up is that ev:event is actually from a different language to XForms. We therefore need to ensure that the namespace for this language is at the top of our document:

<html
 xmlns="http://www.w3.org/1999/xhtml"
 xmlns:ev="http://www.w3.org/2001/xml-events"
 xmlns:xf="http://www.w3.org/2002/xforms"
>
  .
  .
  .

Update your form to include this namespace definition and the changes to the submission element described above, and then reload. Now if you try to post to del.icio.us without any values in the URL or Description fields you should get something like this:

Screenshot of the error message telling the user that they need to provide the required fields

The Textarea Control

The XForms textarea control is used when the amount of text you want your users to enter is quite large, and you want to give them space to compose and manipulate their content. Typical uses would be content management systems, updating blogs, adding comments to a forum and so on. The control itself is used in the same way as the input control, so you can make use of features like hint and help.

In addition to the required fields url and description, the add method in the del.icio.us API has a number of optional parameters. Once of these is called extended, and allows the user to enter a much fuller description of the link.

Let's add a textarea for this, although we'll call it Description, and change the name of our current Description control to Title (it seems easier to understand than extended):

    <xf:input ref="description">
      <xf:label>Title:</xf:label>
      <xf:hint>Enter a <strong>title</strong> for the link</xf:hint>
    </xf:input>
    <xf:textarea ref="extended">
      <xf:label>Description:</xf:label>
      <xf:hint>Enter a <strong>description</strong> for the link</xf:hint>
      <xf:help>
        As well as providing a title for your link, del.icio.us
        allows you to add a full description.
      </xf:help>
    </xf:textarea>

(Note that we've also removed the help text from the description field, since 'Title' seems clear enough now.)

To fit in with the styling that we've been using--one control per row, a dotted line for the bottom border, etc.--you'll also need to add a CSS rule for textarea to the rules for input and output:

        xf\:input,
        xf\:textarea,
        xf\:output
        {
          .
          .
          .

Similarly, we'll need a rule for textarea labels:

        xf\:input xf\:label,
        xf\:textarea xf\:label
        {
          .
          .
          .

and finally, one to set the data-entry area of the textarea:

        .input-value,
        .textarea-value
        {
          .
          .
          .

If you reload your form, it should now look something like this:

Screenshot of the del.icio.us form with an xf:textarea control

Hiding and Revealing Controls

In our del.icio.us form we could make life easier for our users if we didn't show the Title control until the URL field was completed, and similarly we didn't show the Description control until both the URL and Title were filled in. We could also use the same rules to hide the submit button until there was enough data present to make submission possible.

This is possible in XForms by defining rules that say when a control should be relevant or not; it might be that certain fields are only relevant if someone is self-employed or the property they want to buy is a flat. By defining such rules, the controls on the form are revealed in stages as the user fills data in, making errors less likely, and improving the user experience. Let's see how this is done.

Specifying Relevance Rules

We saw earlier how we can use simple rules to indicate that some data values are required, which allowed us to prevent submission unless the values were present:

    <xf:model>
      .
      .
      .
      <xf:bind nodeset="url" required="true()" />
      <xf:bind nodeset="description" required="true()" />
    </xf:model>

A similar technique is used to say when a form control is relevant or not. For example, if the user indicates that they only want to buy a single train ticket, a rule can be set that stops the return date control from being shown.

To make the relevance of the description field dependent on the presence of a value in the URL item, add the following rule to the bind statements:

      <xf:bind nodeset="url" required="true()" />
      <xf:bind nodeset="description" required="true()" relevant="../url != ''" />
    </xf:model>

This simply says that the item description is only relevant if some data has been entered in the item url--i.e., url is not equal to the empty string.

Note the "../" in front of the reference to url; just as it does when changing directories on your disk-drive, ".." means 'go up a level'. We need to do this because the expressions used in @relevant and @required are calculated relative to the value inside @nodeset; since url sits on the same level as description, then to get to it we must go 'up' from description and then come back down again to get to url.

(We'll go into expressions in more detail in a later tutorial, but the long and the short of it is that if we just used the expression url without "../" then we would be making a reference to some data that was underneath the item description, rather than to data at the same level.)

The rule for extended is slightly longer, since we'll make that depend on both the url and description items having data:

      <xf:bind nodeset="url" required="true()" />
      <xf:bind nodeset="description" required="true()" relevant="../url != ''" />
      <xf:bind nodeset="extended" relevant="../url != '' and ../description != ''" />
    </xf:model>

XForms also allows us to tell our submit button to adopt the relevance properties of some named item. This is useful, since without this we wouldn't be able to show and hide buttons in the same way that we do other controls.

The rule we just created for extended--showing and hiding based on whether url and description have data in them--is ideal for the button that sends the data to del.icio.us, and we can use it as follows:

    <xf:submit submission="sub-add-link" ref="extended">
      <xf:label>Search</xf:label>
    </xf:submit>

Now the button will receive all of the same 'special' properties that the Extended control gets.

Styling Relevant and Non-relevant Controls

The result of the relevance rules that we've created is not actually to show and hide the controls, but rather to set a CSS pseudo-class on the controls. The pseudo-classes are :disabled and :enabled.

To hide all of our form controls when they are non-relevant (when their CSS pseudo-class is :disabled) we don't actually need to do anything, since the default CSS rule is:

  .pc-disabled
  {
    display          : none;
  }

Since this is formsPlayer's default we don't need to specify it in our stylesheets, but we're not limited just to showing and hiding controls, and we can use any CSS features. For example, if we would like to apply a yellow fade animation to controls as they become relevant, we can add the following to the list of style rules:

      .pc-enabled
      {
        -event-xforms-enabled : fx-Effect-Highlight();
      }
    </style>

Testing the controls

If you save and refresh your form, you will now see this:

Screenshot of del.icio.us form with only URL field enabled.

Now, to see how relevance works, enter something in the URL control. If you don't have anything convenient, the URL for this handbook is:

http://skimstone.x-port.net/introduction-to-xforms

After you tab away from the control you should see the Title field become available, and it has a yellow background to draw attention to the appearance of the new control:

Screenshot of del.icio.us form with the title field enabled, with fading yellow background.

The yellow background quickly fades away:

Screenshot of del.icio.us form with the title field enabled.

Now add a title. If you are saving a bookmark for this handbook, then the title is:

An introduction to creating web applications with XForms.

This time, after you tab away both the Description control and the Save button should become visible:

Screenshot of del.icio.us form with the description field enabled.

If you're saving a bookmark to this tutorial, your previous entry will be updated. Paste the following to the Description field:

XForms is an exciting new language from the W3C that
can be used to create anything from simple forms to complex Web
2.0 applications. XForms are dynamic, cross-platform, accessible,
script-free...and 100% standard.

This handbook covers everything you need to know to get started
with XForms.

Your form should now look something like this, and you can go ahead and save the link:

Screenshot of del.icio.us form with the submit button enabled.

The Date Control

Another piece of information that del.icio.us can store for us is a date value. We'll add a control for this to our form.

The first thing we need to do is add a new input control after the Description field, and before the Save button:

    </xf:textarea>
    <xf:input ref="dt">
      <xf:label>Date:</xf:label>
      <xf:hint>Enter a date to store with this link</xf:hint>
    </xf:input>
    <xf:submit submission="sub-add-link" ref="extended">

However, if you save and refresh you won't see anything remotely date-related--all you'll get is an ordinary input control. But to get a control that is geared towards accepting dates--a calendar widget--we don't change the control, but the data.

XForms makes extensive use of a model-view-controller (MVC) architecture. There's a lot written on the subject of MVC, and to be honest there's also a lot of disagreement as to the details. But for our purposes the main thing to understand is that it is a 'good thing' if the user interface changes the way it behaves depending on the data it is showing.

XForms does exactly this--if the item referenced by an input control is a date then we get a calendar widget, and if it is a boolean we get a check box. We don't need to change the controls--we just use a normal input--and everything else is done for us. This makes our applications cleaner and easier to maintain.

To indicate that dt is a date, place the following at the end of your current list of bind statements:

      <xf:bind nodeset="extended" relevant="../url != '' and ../description != ''" />
      <xf:bind nodeset="dt" type="xs:date" />
    </xf:model>

The type attribute is used to indicate the type of the data, and since the W3C's XML Schema specification already has definitions for data types like numbers and dates, XForms makes use of it. However, since it is prefixed with xsd we need to ensure that the namespace mapping is at the top of our document:

<html
 xmlns="http://www.w3.org/1999/xhtml"
 xmlns:ev="http://www.w3.org/2001/xml-events"
 xmlns:xf="http://www.w3.org/2002/xforms"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
>
  .
  .
  .

If you make these three changes--add the namespace declaration, the bind statement and the additional input control--and then save and reload the form, you will have something like this:

Screenshot of del.icio.us form with a calendar control

Note that the Date control is visible because we haven't added a relevant rule to hide it, yet. When you've had a good look at the calendar widget and seen what it can do, make a copy of the relevant rule for extended, and use it for the dt node:

      <xf:bind nodeset="extended"
        relevant="../url != '' and ../description != ''" />
      <xf:bind nodeset="dt" type="xs:date" relevant="../url != '' and ../description != ''" />
    </xf:model>

Once data is entered in the URL and Title fields, the other controls will be revealed:

Screenshot of del.icio.us form with a calendar control, after URL and title are entered.

The Select1 control

XForms provides two separate controls for selecting items from a list. The first is select, which presents a list to the user and allows them to choose as many items as they like, whilst the second is select1 which limits the user to one selection.

The del.icio.us add method has an optional parameter called replace which indicates whether the data being submitted should overwrite any existing data. If the value is anything but no then the data will be overwritten. We can provide the user with a way to set this value by using a select1 control.

The select1 control has all of the same features that an input has--such as hint, help, and so on--but in addition supports the item element to provide choices that the user is able to select from. For the del.icio.us form we need two choices, "yes" and "no".

The following mark-up gives us a select1 with these two choices, and a hint to help the user. Add it to your form, just before the submit button:

    </xf:input>
    <xf:select1 ref="replace">
      <xf:label>Replace:</xf:label>
      <xf:hint>
        Set this to <em>no</em> to prevent
        del.icio.us from overwriting a previous entry
        for the same link, if one exists.
      </xf:hint>
      <xf:item>
        <xf:label>Yes</xf:label>
        <xf:value>yes</xf:value>
      </xf:item>
      <xf:item>
        <xf:label>No</xf:label>
        <xf:value>no</xf:value>
      </xf:item>
    </xf:select1>
    <xf:submit submission="sub-add-link" ref="extended">

You'll also need to add the same relevance rule that we use for extended and dt, so that the control is hidden until a URL and a title have been entered:

      <xf:bind nodeset="extended" relevant="../url != '' and ../description != ''" />
      <xf:bind nodeset="dt" relevant="../url != '' and ../description != ''" />
      <xf:bind nodeset="replace" relevant="../url != '' and ../description != ''" />
      <xf:bind nodeset="dt" type="xsd:date" />
    </xf:model>

And finally, to get the formatting right, you'll also want to add to the CSS rules:

        xf\:input,
        xf\:select1,
        xf\:textarea,
        xf\:output
        {
          .
          .
          .
        }

        .input-value,
        .select1-value,
        .textarea-value
        {
          .
          .
          .
        }

        xf\:input xf\:label,
        xf\:select1 xf\:label,
        xf\:textarea xf\:label
        {
          .
          .
          .
        }

If you save the form and then refresh it in the browser, your form should look something like this:

Screenshot of del.icio.us form with a drop-box

The appearance attribute

The default display of the select1 that we just added was a drop-box. Howevever, often when there are only a couple of choices we would rather have them displayed as radio buttons. This is easily done in formsPlayer by adding the appearance attribute and giving it a value of full:

    <xf:select1 ref="replace" appearance="full">
      <xf:label>Replace:</xf:label>
      .
      .
      .
    </xf:select1>

Just to be clear, this attribute doesn't mean 'please give me radio buttons'--instead it informs an XForms processor that the author would like as many of the choices in the list to be available to the user as possible. Of course, how that actually pans out and gets implemented will be different on different systems; the effect of this on a voice-based XForms processor will be different to the effect on a GUI-based one, and different again to a mobile phone.

But the idea is that there is at least some similarity in the way this is implemented, and the author is in effect providing a 'hint' to the processor about what they would like to happen. In the case of formsPlayer, when appearance is set to full on a select1 then all of the options in the list are made available via radio buttons:

Screenshot of del.icio.us form with radio buttons

Setting Initial Values

Recall that the replace item is used to tell the del.icio.us servers whether it should overwrite any previous entry for the link we are submitting. We created a couple of radio buttons to allow us to set this value, but if you ran the form, or looked carefully at the screenshot, you might have noticed that none of the radio buttons had an initial value.

As it happens this is fine for del.icio.us, since it doesn't actually need this value--replace is only significant if it is present and set to "no". Still, so that we have something that we can use to illustrate the points, we'll pretend that the value of replace is important, and that we need to set a default value of "yes".

To set the value of an item in XForms, we use an action handler, called setvalue. (You met 'action handlers' before, when we looked at message.) The handler takes the ref attribute to indicate what item should have its value set, and the content of the element indicates the value that the item will be set to. In our case we want to set the value of replace to "yes", so we'll do this:

<xf:setvalue ref="replace">yes</xf:setvalue>

As with the message handler, this action won't actually do anything until it gets a signal. In the message example the 'signal' we chose to register for was the notification event that is given whenever there is a submission error. For our setvalue we'll also register for a notification event, and this time it's xforms-ready which is fired by an XForms processor once initialisation is complete. Place the following mark-up just before the closing tag of the model element:

      <xf:bind nodeset="dt" type="xsd:date" />
      <xf:setvalue ev:event="xforms-ready" ref="replace">yes</xf:setvalue>
    </xf:model>

Once again, save and reload and your form, and notice that the Replace control is initialised to "Yes".

The Select control

Whilst the select1 control allows the user to choose one option from amongst many, the select control allows multiple options to be picked. We'll illustrate the use of this control by providing a way for users to indicate the subject of the link. To begin with we'll assume that any links we save are about XForms, skiing or both:

  </xf:textarea>
  <xf:select ref="tags" appearance="full">
    <xf:label>Tags:</xf:label>
    <xf:hint>Enter some <em>tags</em> for the link</xf:hint>
    <xf:item>
      <xf:label>XForms</xf:label>
      <xf:value>xforms</xf:value>
    </xf:item>
    <xf:item>
      <xf:label>Skiing</xf:label>
      <xf:value>skiing</xf:value>
    </xf:item>
  </xf:select>
  <xf:input ref="dt">

As with the other controls we'll only allow the user to enter data into this one when a URL and title have been provided:

  <xf:bind nodeset="extended" relevant="../url != '' and ../description != ''" />
  <xf:bind nodeset="tags" relevant="../url != '' and ../description != ''" />
  <xf:bind nodeset="dt" type="xs:date" relevant="../url != '' and ../description != ''" />

Finally, we need to update the CSS rules:

        xf\:input,
        xf\:select,
        xf\:select1,
        xf\:textarea,
        xf\:output
        {
          .
          .
          .
        }

        .input-value,
        .select-value,
        .select1-value,
        .textarea-value
        {
          .
          .
          .
        }

        xf\:input xf\:label,
        xf\:select xf\:label,
        xf\:select1 xf\:label,
        xf\:textarea xf\:label
        {
          .
          .
          .
        }

Save the form, refresh your browser, and then enter a URL and a title, and when the hidden controls are revealed you should see two check-boxes for adding tags.

HOWTO: Searching Flickr

Flickr is well known both as a full-featured image storage system, and one of the web applications that got everyone talking about Web 2.0 and Ajax.

In the following section we'll make use of the powerful Flickr API to search for a list of photos that have a particular tag on them. We'll also demonstrate how to show and hide parts of the application based on its state, as well as how to animate the display when these states change.

(If you want to get the completed source code, it's available here.)

Tutorial reviewed 2006-12-06, by MB.

Screenshot of Flickr search form showing flowers as search results.

Getting Started

The first thing we need to do is create an empty XHTML document into which we can place the mark-up for the Flickr seach form. If you created the template at the beginning of the Introduction to XForms then go ahead and use that. Otherwise, create a new document in your editor and copy the template from here. Change the title to "Flickr Search Form", and save the file as flickr.html.

The finished form also requires at least version 1.4.2.1016 of formsPlayer, so if necessary, change the object tag, as follows:

   <head>
      <object width="0" height="0" id="formsPlayer"
         classid="CLSID:4D0ABA11-C5F0-4478-991A-375C4B648F58"
         codebase="http://skimstone.x-port.net/files/releases/formsPlayer-1.4.2.1016.cab#Version=1,4,2,1016"
      >
         <b>formsPlayer has not been installed.</b>
      </object>

We'll also create a separate CSS file to hold our style rules so add the following line to the head:

    <title>Flickr Search Form</title>
    <link rel="stylesheet" href="flickr.css" type="text/css" />
  </head>

And then create an empty file, called flickr.css. We're ready to go.

Creating the Form

The application is going to be made up of two parts. The first is a small form that will allow a user to enter a search term and then press a button to perform the search.

The second part will contain a repeating structure that will automatically take each photo element returned from the search, and uses it to create a reference to the actual image on the Flickr servers.

To create the search form, add the following to the body element:

  <body>
    <xf:group id="main-body">
      <xf:input ref="tags">
        <xf:label>Tags:</xf:label>
      </xf:input>
      <xf:submit>
        <xf:label>Find</xf:label>
      </xf:submit>
    </xf:group>
  </body>

This gives us a simple input control with a button next to it. We've placed the two controls inside a group mainly to make styling easier. Add the following style rules to your flickr.css file:

body
{
  font-family           : "Trebuchet MS", Verdana, Helvetica, Sans-Serif;
  font-size             : 11px;
}

#main-body
{
  margin                : 0;
  background-color      : #eee;
  border                : 5px solid #333;
  padding               : 15px;
  display               : block;
}

Save both of your files and load flickr.html in the browser, and you should have something like this:

Screenshot of the Flickr search form

Using the Flickr API

Flickr provides a number of different services for interacting with its data. If you look at the list of methods that the Flickr API supports, you'll see they are divided into groups; one group of API methods deals with photos, another with people, and so on.

Flickr can accept requests to these methods in a number of different formats, all of which we can use with XForms. The easiest to understand and test is REST, and to use it to make a call to any one of the methods listed we need to go through the following URI:

http://www.flickr.com/services/rest/

The parameters that you must pass are the method you want to call, and a special application key. The key must be obtained from Flickr, and although we have one for this tutorial, if you plan to develop the sample form further you should get your own key.

The particular method we will use for this form is flickr.photos.search, and you can see from the documentation that this method takes a number of parameters. Of the optional parameters, the only ones that we'll use are tags and per_page.

The tags parameter contains the tag or tags that the user of our form wants to search for, whilst per_page indicates how many items 'per page' we would like to be returned; since there may be tens of thousands of items that match a particular tag, Flickr will give the list to us one page at a time, and this parameter indicates how many items we would like on each of those pages.

Combining these two method-specific parameters with our general ones, and using the 'REST URL', we have a resulting call to the method that looks like this (the extra line breaks are to make it easier to read):

http://www.flickr.com/services/rest/?
  method=flickr.photos.search&
  api_key=68149024a667e0be3c63708f002ffe1e&
  tags=dogs&
  per_page=12

The results of such a call might be something like this (try it):

<rsp stat="ok">
  <photos page="1" pages="983" perpage="24" total="98265">
    <photo id="94048829" owner="54228211@N00" secret="9b2e52936b"
     server="37" title="These paws are made for walking - 5997"
     ispublic="1" isfriend="0" isfamily="0"
    />
    <photo id="94044468" owner="23819962@N00" secret="1641bc41c5"
     server="41" title="listen up dogs"
     ispublic="1" isfriend="0" isfamily="0"
    />
    .
    .
    .
  </photos>
</rsp>

All data from Flickr is structured in much the same way as this; the containing element is always rsp, and there is always a stat attribute that indicates whether the request was successful or not.

What is inside the rsp element will change from method to method, but if the result is a set of photos, then they will always be in a list like this. Note that there is actually no URL for the image itself in the set of results, since it is our job to create it by combining together the information passed to us in the photo element. This is because Flickr stores multiple formats of the same image, and we'll see how to combine the pieces later.

Setting Up the Search

In our del.icio.us form we saw that all of the parameters we needed had a corresponding form control. We could do the same here, but it would mean creating a form control for things like the method and api_key which are not pieces of information that we want our users to be able to update. To create a nice easy to use form for searching Flickr, we only really need one form control, and that is for the tags value; all the other fields can be given fixed values.

XForms Data Islands

To achieve this we need to create an XForms data island that will hold all of our items. This may seem a little different to how we worked with the del.icio.us example, but in fact it isn't--all that happened with the del.icio.us form was that formsPlayer created a 'default' data island for us since we didn't specify one explicitly. And it used the form controls that we had provided to 'work out' how the 'automatic' data island should be structured. (This is called 'lazy authoring', for obvious reaons.)

Let's create a data island that contains the items we need for our search on Flickr (instances are placed inside an XForms model):

    <link rel="stylesheet" href="flickr.css" type="text/css" />
    <xf:model>
      <xf:instance>
        <instanceData xmlns="">
          <method>flickr.photos.search</method>
          <api_key>68149024a667e0be3c63708f002ffe1e</api_key>
          <tags />
          <per_page>12</per_page>
        </instanceData>
      </xf:instance>
    </xf:model>
  </head>

Hopefully this all looks familiar to you, since all we have created is one XML item for each of the parameters that Flickr needs, and given each a value should it need one. Note that we've also created an empty entry for tags, even though we have a form control that refers to it. This is because the automatic creation of 'default' items only happens if we don't provide an instance; but once you do create one, then you will have to include within it any items that you need for your controls. However, the input control that we created earlier will correctly reference the tags item in this instance, just as before it was referencing the one that was created automatically.

Specifying a Separator for Submission

Now that we have set up a data island ready to send to Flickr, we can go on to prepare the request that will be used to indicate where to send everything. You saw earlier how to specify a request to del.icio.us, and we need to do much the same here:

      </xf:instance>
      <xf:submission id="sub-flickr"
       method="get" action="http://www.flickr.com/services/rest/"
       separator="&amp;"
      />
    </xf:model>

The only difference you will see is that we had to explicitly say that the separator value is "&amp;", since with XForms the default is ";". (We didn't bother to do this for del.icio.us, since their servers understand both values.)

Save the form, refresh, type a value into the input control, and press the "Find" button, and you should get something like this:

Screenshot of XML returned from Flickr.

We mentioned in an earlier section that replacing the form with the data returned from the server was the default behaviour for XForms, making it exactly the same as HTML forms. In the next section we'll show how to make use of the XML without replacing the entire form, but before we do, go 'back' in the browser to your form, and try pressing "Find" again without entering a tag; note that this time you should get an error message from Flickr:

Screenshot of XML returned from Flickr when there is an error.

Capturing the XML Returned by Flickr

The whole point of using the Flickr API is so that we can make use of the XML returned. Unlike HTML forms where the page will be replaced by whatever comes back from the server, XForms allows us to retrieve data from a server, store it locally and then manipulate it.

In order to manage the parameters that were to be sent to Flickr we created an instance in the model. We'll do the same now to manage the data that is returned from Flickr:

      </xf:instance>
      <xf:instance id="inst-rs">
        <dummy xmlns="" />
      </xf:instance>
      <xf:submission id="sub-flickr"

We've given the instance a dummy value since it will be replaced anyway by whatever comes back from Flickr.

Now we can modify the request so that instead of replacing the entire form--the default behaviour--the returned data is placed into our new instance. This is easily done by adding the following two attributes to the submission:

      <xf:submission id="sub-flickr"
       method="get" action="http://www.flickr.com/services/rest/"
       separator="&amp;"
       replace="instance" instance="inst-rs"
      />

(Note that the default value for replace is all, which is the 'HTML form'-style behaviour that we have been seeing, where the entire form is replaced with whatever the server returns.)

If you refresh your form and try searching again it appears that nothing has happened! This is because the data from Flickr is now being stored in the instance inst-rs, and the form is no longer being replaced. The next step is to do something with the data.

The Output Control

The output control has much the same structure as the input control, which means we can add labels and hints for example. But as you'd probably guess, it's not an interactive control--it's just used to get some data out of the instance so that the user can see it.

We'll use an output control as a simple way to show any error text that comes back from Flickr. Add the following just after the submit button:

      </xf:submit>
      <xf:output ref="instance('inst-rs')/err/@msg">
        <xf:label>Error:</xf:label>
      </xf:output>
    </xf:group>

Note that we have to use the instance function since there is more than one instance our form--one for specifying the parameters to send, and one to hold the response from Flickr--and formsPlayer will always use the first one unless told to do otherwise.

You'll no doubt be wondering what is going to happen here if there is no error string returned from Flickr. The answer is that in XForms if you add a form control and connect it to an item that does not exist, then by default it is regarded as non-relevant--and as we saw earlier, relevance is used to show and hide controls, so our control will simply not appear unless there is an error message.

(Note that if you don't create an instance then items will never 'not exist' since formsPlayer will create a set of default items for you. If you are not clear on why this is, you might want to revisit the earlier discussion about 'lazy authoring'.)

In our example, our output form control will automatically be given the CSS pseudo-class :disabled if there is no error (i.e., the item err/@msg does not exist), and it will have the class :enabled if there is an error. This is a very simple and convenient way to have the conditional display of items, and it means that we can specify CSS style rules that take advantage of these automatic class changes. In this case we don't need anything, since the default style rule for :disabled is display: none;.

If you reload your form and press "Find" with nothing in the Tags: control, you should get the following:

Screenshot of a search on the Flickr form that results in an error.

Just to spell out what is happening, Flickr is giving us back exactly the same data that you saw before, but now instead of wiping out our form, formsPlayer is storing the returned XML in a data island. An output control is 'wired up' to show a specific item in the data island if it exists, and if it doesn't, the control is hidden.

The Repeat Control

A repeating structure usually involves defining a list of items to 'repeat over' (or iterate) and a template to be applied to each item in the list.

In many systems the template part would need to be defined with a separate file, and although there will be certain situations where this is a good approach, there are also plenty of situations where it's just that bit too much work for the simple job in hand.

The XForms repeat element allows us to specify both the list and template in one. The items that make up the list are expressed using an XPath expression, and the template is defined by placing mark-up inside repeat. (The mark-up can be anything, including further repeat elements.)

Specifying the Items for Repeat

For the Flickr form we want a repeating structure that has as many entries as there are photo items in the returned XML. The XPath to create a list of photo items is:

instance('inst-rs')/photos/photo

We can use this expression to set up a repeating structure that operates on each returned photo, by adding the following to the form just after the output that shows any error results:

      </xf:output>
      <xf:repeat nodeset="instance('inst-rs')/photos/photo">
        [...]
      </xf:repeat>
    </xf:group>

The Repeat Template

The template is created from any tags that are inside the repeat element itself, and can include any HTML and XForms tags, including the repeat element. A moment ago we set the template to a simple ellipsis:

      <xf:repeat nodeset="instance('inst-rs')/photos/photo">
        [...]
      </xf:repeat>

This will give us enough to test with, so save and refresh the form, and try searching for something--you should see as many ellipses as there are search results:

Screenshot of Flickr search form showing search results...but only the image titles

Now that we know that both the search and our repeat are working fine, let's fill out the repeat template.

We'll first use a simple output control to show the title of each of the photos returned from Flickr, before we then move on to show how to display the actual image.

XPath Evaluation in Repeat Templates

The XML item that holds this data is the title attribute on each photo element. The XPath expression needed to refer to, say, the title of the third photo would be:

instance('inst-rs')/photos/photo[3]/@title

However, when we place controls into a repeat template, their XPath expressions are evaluated one at a time, in the context of each of the items in the list. So to show the title of each of the photos in the list, we need only do this:

      <xf:repeat nodeset="instance('inst-rs')/photos/photo">
        [<xf:output ref="@title" />]
      </xf:repeat>

Refresh the form, and try searching for something--you should see a list of image titles:

Screenshot of Flickr search form showing search results...but only the image titles

Displaying the images

Flickr doesn't give us a URL for each photo since it stores many different formats of posted images. Instead, when we search, Flickr provides us with all the bits we need to create the URL of the image we want, using a set of predefined rules. This allows us to choose from a number of image formats, depending on the context.

To display each image, we therefore need to calculate the URLs and then make use of the URL to create an image in the form. We'll now show how to do that.

The Output control and calculated expressions

The first thing we need to do is create the full URL for each image. The URL consists of the Flickr domain name, a number for a server to retrieve the image from, an ID, a 'secret number', and a size:

http://static.flickr.com/{server}/{id}_{secret}_{size}.jpg

The following mark-up creates a URL like this for each photo in the search results:

      <xf:repeat nodeset="instance('inst-rs')/photos/photo">
        <xf:output
         value="concat('
            'http://static.flickr.com/',
            @server, '/',
            @id, '_',
            @secret, '_s.jpg'
         )"
        />
      </xf:repeat>

The main new feature to note is that we're using the attribute value on our output control, rather than the ref attribute. The distinction is that ref refers to an item in an instance, whilst value can be any calculated expression.

In the value attribute we have an XPath expression that is making use of the concat function. All this function does is to join together all of its arguments to produce a string:

concat(
  'http://static.flickr.com/',
  @server,
  '/',
  @id,
  '_',
  @secret,
  '_s.jpg'
)

This is an XPath function that combines each of its parameters together to create a string. If any of the parameters are themselves XPath expressions then they are evaluated first, so in this case the values for the attributes server, id and secret are obtained from each photo and placed into the result.

The Output control and images

Now that the output control has access to the calculated URL, we can make use of it to load the desired image. To do this, all we need to do is add the mediatype attribute which indicates that the data in the output is not just to be used as plain text for display, but is a reference to some other kind of object. The values of @mediatype will usually be MIME types, so to indicate that we want an image we simply use image/jpeg or image/*.

      <xf:repeat nodeset="instance('inst-rs')/photos/photo">
        <xf:output
         value="concat('
            'http://static.flickr.com/',
            @server, '/',
            @id, '_',
            @secret, '_s.jpg'
         )"
         mediatype="image/*"
         class="image"
        />
      </xf:repeat>

You'll see that we also added a @class whilst we're here; this is so that we can make the images a little neater by adding the following style rule to the end of flickr.css:

xf\:output.image
{
  border                : 1px solid black;
  width                 : 75px;
  height                : 75px;
  margin                : 5px;
}

Note the use of the class "image" that we set on the output; this is so that other uses of output don't get displayed as 75x75 squares.

Now, save, refresh and search, and you should have results that look something like this:

Screenshot of Flickr search form showing flowers as search results.

Switch and Case

Our form is working fine so far, but we can improve on the user experience by having different parts of the form active at different stages.

For example, once a search is underway it would be nice to hide any previous images or error messages, and instead show some kind of indication that the search is 'in progress'. We can do this by making use of switch and case.

The switch element provides a container for a set of cases, only one of which will be active at any one time. We'll see exactly how to select a particular case in a moment, but first add the following mark-up after the submit button:

    </xf:submit>
    <xf:switch>
      <xf:case id="case-start">Start</xf:case>
      <xf:case id="case-busy">Busy</xf:case>
      <xf:case id="case-done">Done</xf:case>
    </xf:switch>
    <xf:output ref="instance('inst-rs')/err/@msg">

If you reload your form you will see the word "Start" just below the input control, with "Busy" and "Error" hidden because only one case can be active at a time.

The Toggle Action

The next step is to change the active case as the user interacts with the application. The 'action' to do this is called toggle and it takes a single value, the id of the case to make active:

<xf:toggle case="case-busy" />

You might be tempted to think that the 'busy' case should be made active when the user clicks on the button, and this is indeed what many non-MVC systems would do. But XForms goes to great lengths to decouple the data and the user interface, and it is much better to change the active case to 'busy' when the submission begins, as indicated by the xforms-submit event.

By doing this we tie our 'busy' state to the request to Flickr, rather than some action performed by the user, and it's good to get into this habit; more advanced tutorials will show how to send data to a server as a result of other activities in the form, not just mouse clicks.

Just as we're going to use the xforms-submit event to select the 'busy' state, we'll use the xforms-submit-done event (dispatched when submission has completed successfully) to select the 'done' state. Modify the submission element to include our two action handlers:

      <xf:submission id="sub-flickr"
       method="get" action="http://www.flickr.com/services/rest/"
       separator="&amp;"
       replace="instance" instance="inst-rs"
      >
        <xf:toggle case="case-busy" ev:event="xforms-submit" />
        <xf:toggle case="case-done" ev:event="xforms-submit-done" />
      </xf:submission>

Save your changes and refresh your form, and you should see "Start" below the input control. Then each time you search you should see "Busy" as the search begins, and "Done" when it completes.

Showing an 'In Progress' Animation

Our next step is to show an image to the user to indicate that the search is in progress. Of course you can put any mark-up you like in the "Busy" case, but we're going to show an animated image to give the user a sense of something happening. An image is attached to this page, and you will need to copy it to the same directory as your form. Then replace the text in the case with an img tag:

        <xf:case id="case-busy">
          <img src="spinner.gif" alt="Busy" />
        </xf:case>

Since we've already configured this case to be toggled when a submission begins, the user will see the animated image the moment they press the "Find" button.

Using Group to Set Evaluation Context

Next we'll move the error message and repeat into the "Done" case. However, as we do we'll make a slight modification; you probably noticed that the XPath expressions in output and repeat both begin with a call to the instance function:

<xf:output ref="instance('inst-rs')/err/@msg">
  <xf:label>Error:</xf:label>
</xf:output>
<xf:repeat nodeset="instance('inst-rs')/photos/photo">
  ...
</xf:repeat>

You may have wondered if it was possible to factor this out, and set a common context for both expressions, and it is.

Although groups can be used simply to establish a common area for a set of controls, another use for them is to set an evaluation context for a whole set of XPath expressions. This makes them not only easier to type but also to maintain, since if the data source changes it is easier to move controls around if their expressions are shorter.

For example, if you had an address item that had further items nested underneath, for street, city, country, and so on, you could set the context to be the address item, and then the expressions for each of the contained controls would be shorter.

To illustrate this, as we move the controls into the "Done" case we'll add a group to set context:

        <xf:case id="case-done">
          <xf:group ref="instance('inst-rs')">
            <xf:output ref="err/@msg">
              <xf:label>Error:</xf:label>
            </xf:output>
            <xf:repeat nodeset="photos/photo">
              <xf:output
               value="concat('
                &lt;img src=&#34;http://static.flickr.com/',
                @server, '/',
                @id, '_',
                @secret, '_s.jpg&#34; /&gt;'
               )"
               class="image"
              />
            </xf:repeat>
          </xf:group>
        </xf:case>

Setting the context won't have any effect on the way the form works, but moving everything into the "Done" case has; if you reload your saved form and click on "Find", any previous search results will be hidden, replaced by the 'in progress' animation, and once the search is complete, the "Done" state will be toggled in (showing either the images or an error message).

Animating the Display

The next step is to have some kind of animation when the images are ready to be viewed, such as the whole panel sliding into view.

We can achieve this easily by adding a CSS rule that tells formsPlayer to slide down a case when it receives the xforms-select event.

the following to the end of flickr.css:

xf\:case
{
  -event-xforms-select   : fx-Effect-SlideDown(duration:1);
}

Note that there is a bug in Internet Explorer which gives us a slight flicker before the animation begins. We can work around this by overriding the default rule that hides deselected cases, and replacing it with a rule that performs a slide up on receiving the event xforms-deselect. The resulting CSS is:

xf\:case
{
  -event-xforms-deselect : fx-Effect-SlideUp(duration:0);
  -event-xforms-select   : fx-Effect-SlideDown(duration:1);
}

Now, whenever an XForms case element receives the xforms-select event, a slide-down animation will be initiated, set to take 1 second. Similarly, whenever the xforms-deselect event occurs, a slide-up animation will be initiated.

Save the form, refresh and try searching; once the results are available they should slide into view, and each time you try a new search the previous results should be immediately hidden, before the new ones slide in again.

The Range Control

The range control is the XForms equivalent of a 'slider'. The XForms version allows us to indicate start, end and step values. We'll use a slider to allow the user to control the size of the images that are displayed.

The first step is to create some instance data to hold the current size of an image. In our applications we often have an instance called control that we use to hold assorted values like this, so add the following to the model:

      </xf:submission>
      <xf:instance id="inst-control">
        <instanceData>
          <size>75</size>
        </instanceData>
      </xf:instance>
    </xf:model>

This gives us an element called size with an initial value of 75. We use this to start us off, since it is the size of the smaller images on Flickr that we have been using so far.

Next we add our range control. As with the other controls you have seen, we must have a label, but we can also optionally have help, hints, and so on. The range control supports start and end attributes to indicate the 'range' of possible values. We'll use 1 to 200, so as to allow our images to go from 1px up to 200px:

      </xf:submit>
      <xf:range
       ref="instance('inst-control')/size"
       start="1" end="200"
       style="width: 200px;">
        <xf:label>Size:</xf:label>
      </xf:range>
      <xf:switch>

The range control can only be used on data that is typed to be an integer. We saw how to set the data type of a node in the del.icio.us form when we set an element to be a date, and we will use the same technique here. Add a bind statement to the model, below our control instance:

      </xf:instance>
      <xf:bind nodeset="instance('inst-control')/size" type="xs:integer" />
    </xf:model>

Obtaining intermediate values from the range control

If you play with the form so far, you will have noticed that the images only resize after you have finished dragging the slider. [This part of the tutorial is being changed. In a previous version you could resize the images with the range control, but the technique to achieve this was non-standard. This is being changed.] This is fine for many situations, but doesn't feel quite right for tasks relating to the images, since more often than not you will drag the slider until the images look right, rather than paying attention to the value.

We can easily make our application resize the images as you drag by using the incremental attribute. This tells formsPlayer to periodically update the data value in size, rather than waiting until the user has finished dragging. The same technique can be used in any other form controls; you might use it in an input control to provide 'suggestions' to the user as they type, for example.

The attribute is set as follows:

      <xf:range
       ref="instance('inst-control')/scale"
       incremental="true"
       start="1" end="200"
       style="width: 200px;">
        <xf:label>Size:</xf:label>
      </xf:range>

Now try dragging the control.

The if() function

We saw ealier with the use of concat() how expressions can contain calls to functions. Another very useful function is if() which works much like it does in spreadsheets; if the first value is true then the second value is returned, otherwise the third value is returned.

We'll make use of this in the part of our application that works out the URL for the photo. Recall that we created the URL of the photo by joining various parts of the data that Flickr provided us with. One of these parts is a single letter to indicate what size of image we would like, with the choices being s, m and l. Let's avoid some of the distortion we see when we make the images too large by using the medium-sized image if the user takes the size over 100px:

              <xf:output
               value="concat(
                 'http://static.flickr.com/',
                 @server, '/',
                 @id, '_',
                 @secret,
                 '_',
                 if(
                   instance('inst-control')/size &lt; 100,
                   's',
                   'm'
                 ),
                 '.jpg',
                '/&gt;'
               )"
               class="image"
              />

Conclusion

Even though this introduction barely scratched the surface of what the combination of XForms, XHTML and CSS has to offer, you've already learned enough to build complex user interfaces.

Other tutorials will show how to build standalone desktop applications, browser extensions, and more.

Further reading

If you'd like to read more introductory material before continuing with this book, try some of these:

Action handlers

XForms contains a number of different action handlers that can be used to perform tasks such as navigating to a new document, toggling a case, and so on.

Controlling performance of switch and case.

formsPlayer 1.5.7 sees the introduction of new features to allow form authors to decide when the contents of a case element will load. This gives the form author finer control over the performance of an XForms document, by allowing them to choose whether to load some content at startup, or later on in the lifetime of a form instance. Until now, in 1.5 builds, the markup within a case element has been ignored until that case is first selected, giving potentially faster load times, but a slower reaction to the toggle action.

There are three new ways to ensure that a case element is ready prior to toggling into that case.

1. script

A case can now be preloaded through script- This could be actuated through some user interaction, or an event such as xforms-submit, to prepare the case for when it is needed.

<a href="#" onclick="document.getElementById('c1').loadContent();">Preload xf:case[@id="c1"] using some script</a>

2. case/@appearance

If a switch contains a case which is almost always used, but is not the default case, and other cases are rarely used, that case can be loaded at startup thus:

<xf:case appearance="full">
...
</xf:case>

3. switch/@appearance

If all cases within a switch are likely to be used during a visit to the form, then the contents of all cases can be loaded at startup, by setting the appearance of the switch to full, thus:

<xf:switch appearance="full">
...
</xf:switch>

Manipulating data with handlers and script

XForms provides a number of action handlers that can add and remove nodes in the instance data, as well as change the values of nodes. It's also possible to gain access to the instance data via an XML DOM so that values can be changed through script, if necessary.

Inserting and deleting nodes

There are a number of ways to insert and delete nodes in XForms, based around the action handlers insert and delete.

The insert action handler

insert allows a node to be inserted into a nodeset, at a certain position. The handler has a set of default behaviours defined which creates for a very powerful and flexible action.

All of the following examples assume this initial instance data:

<instanceData xmlns="">
  <list>
    <y>y1</y>
    <y>y2</y>
    <y>y3</y>
  </list>
  <templates>
    <x>x1</x>
  </templates>
</instanceData>

Duplicating the last node in a nodelist

The simplest form of the handler specifies a nodelist, and duplicates the last item in the list:

<xf:insert nodeset="list/y" />

The result would be:

<list>
  <y>y1</y>
  <y>y2</y>
  <y>y3</y>
  <y>y3</y>
</list>

Specifying the position to duplicate to

It is also possible to indicate a position to copy the last node in the list to. The at attribute indicates which node in the nodeset to place the copied node after:

<xf:insert nodeset="list/y" at="1" />

The result would be:

<list>
  <y>y1</y>
  <y>y3</y>
  <y>y2</y>
  <y>y3</y>
</list>

To place the copied node before the node referred to by @at, use the position attribute with a value of before:

<xf:insert nodeset="list/y" at="1" position="before" />

The result would be:

<list>
  <y>y3</y>
  <y>y1</y>
  <y>y2</y>
  <y>y3</y>
</list>

Note that the default value for @position is after, so the following are equivalent:

<xf:insert nodeset="list/y" at="1" />
<xf:insert nodeset="list/y" at="1" position="after" />

The @at attribute is actually an XPath expression, so it is possible to calculate an insert position at run-time. The expression is evaluated in the context of the first node in the @nodeset, as illustrated by the following example:

<xf:insert nodeset="list/y" at="count(../y) - 1" position="before" />

The result would be:

<list>
  <y>y1</y>
  <y>y3</y>
  <y>y2</y>
  <y>y3</y>
</list>

Copying a node other than the last in the list

It is also possible to copy a node from some other location than the last node in the list, by using the origin attribute. The node could be in the target nodelist:

<xf:insert nodeset="list/y" origin="list/y[1]" />

which would give:

<list>
  <y>y1</y>
  <y>y2</y>
  <y>y3</y>
  <y>y1</y>
</list>

But the node to copy need not be in the nodeset being updated, and could come from some other location, such as a collection of templates:

<xf:insert nodeset="list/y" origin="templates/x" />

The result would be:

<list>
  <y>y1</y>
  <y>y2</y>
  <y>y3</y>
  <x>x1</x>
</list>

Initialising empty nodesets

If the nodeset referred to is empty then the node being copied is created as a child of the insert handler's context node (if you are not clear on the evaluation context then read this):

<xf:group ref="list">
  <xf:repeat nodeset="y">
    <xf:output ref="." />
  </xf:repeat>

  <xf:trigger>
    <xf:label>Add new 'x'</xf:label>
    <xf:action ev:event="DOMActivate">
      <!--
        The evaluation context here is still 'list', from
        the xf:group, above.
      -->
      <xf:insert nodeset="y" origin="../templates/x" />
    </xf:action>
  </xf:trigger>
</xf:group>

In many situations there may be no natural 'context', such as the group in the previous example. In these circumstances we use the context attribute to set a context explicitly:

<xf:insert context="list" nodeset="y" origin="../templates/x" />

Note that the @origin value is also evaluated relative to the evaluation context.

Manipulating instance data with script

XForms provides the getInstanceDocument method to allow access to instance data within a model. An example of its use is here.

Sending and receiving data with submission

The XForms submission module provides many different ways to read data into a form, and to send processed data back out again.

An introduction to XForms submission

This introduction contains a high-level look at XForms submission. For those new to a declarative approach to programming it's probably easier to understand what XForms submission does by looking at the kinds of procedural code it replaces. Since the approach most familiar to web programmers will be the use of JavaScript in an Ajax application we'll begin there, before then moving on to XForms.

Ajax submission

Ajax emphasises asynchronous programs, by way of JavaScript. Programmers used to languages such as Java or C++ will probably find the idea quaint. But as the explosion of interest in Ajax in the last few years has shown, programmers using HTML forms as their application's front-end were crying out for a way to remove unnecessary round-trips to the server and page refreshes.

There are a number of ways this can be achieved in Ajax programming, but the most common is to avoid using HTML forms, and instead use a separate object which can retrieve data directly. This data can then be used to update small parts of the display, and of course dramatically improves the user experience.

The script

Recall that the only reason we're looking at the way data is transferred using script, is as a way to understand what it is that XForms is making easier. If you're looking at XForms with an Ajax background, then this will be useful information, but if you've not used script or Ajax before, don't feel the need to learn any of the following constructs--just try to get a high-level view.

The most common way of doing an Ajax request is to set up script like this:

var req;

function loadXMLDoc(url) {
  // native XMLHttpRequest object
  if (window.XMLHttpRequest)
  {
    req = new XMLHttpRequest();
    req.onreadystatechange = readyStateChange;
  // IE/Windows ActiveX version
  } else if (window.ActiveXObject)
    req = new ActiveXObject("Microsoft.XMLHTTP");

  if (req);
  {
    req.onreadystatechange = readyStateChange;
    req.open("GET", url, true);
    req.send();
  }
}

function readyStateChange() {
  // '4' means document "loaded"
  if (req.readyState == 4)
  {
    // HTTP 200 means "OK"
    if (req.status == 200)
    {
      // do something here
    } else
    {
      // error processing here
    }
  }
}

loadXMLDoc("http://example.com/customers/my-customer.xml");

A more complete approach would look to handle the error conditions, allow more than one submission to be running at the same time, and so on. But since that would make the code more complicated, not less, the snippet we have gives us plenty to help us understand what it is that a data submission is generally doing.

The XForms equivalent

Let's map the procedural code that we've just seen to the XForms equivalent:

<xf:submission method="get" action="http://example.com/customers/my-customer.xml">
  <xf:action ev:event="xforms-submit-done">
      <!-- do something here -->
  </xf:action>
  <xf:action ev:event="xforms-submit-error">
      <!-- error processing here -->
  </xf:action>
</xf:submission>

Here we can see that we've specified the same things that we did in the Ajax approach--the URL that we want to get data from, the actions to perform if there is an error, and so on--but the difference here is that this is all we've had to do. Everything else involved in the submission has been done for us.

Submission as a pattern

This idea of simply 'filling in the blanks' will be very familiar to anyone who takes a 'pattern' approach to their development. By re-using commonly occurring programming 'patterns', programmers are able to develop applications more quickly and more accurately. A pattern is not necessarily a library of code though, but is more a set of relationships between high-level, abstract components.

Let's think of our XForms submission processing as a pattern. What we have is some behaviour that is defined in a pretty abstract way, along the following lines:

'get' some data from one URL, or 'put' some data to another URL, and when done, generate either a 'success' event or a 'fail' event.

The strength of any pattern is in the fine balance between being sufficiently general that it can apply to many situations, but not so general that it says nothing. In this case it does seem quite vague; all we seem to have said is 'get a document from here and put it there, and let me know when you've finished'. But when you think about it, that pretty much covers most of the basics for a lot of things we'd like to do: sending an email, saving a file to disk, retrieving an RSS feed, posting a document to a server, and so on. It certainly captures the basic Ajax request we looked at earlier.

By operating at such a high-level of abstraction, we provide a lot of power to the programmer whilst also reducing the amount that they need to learn to perform new tasks.

Reading data from files

To illustrate, let's look at the XForms mark-up for retrieving a file from disk:

<xf:submission
 method="get" action="file:my-customer.xml"
/>

As you can see, unlike with other programming paradigms, no new API is needed to make the shift from HTTP to the file system. In other words, any author/programmer who mastered the most common technique--getting and putting data to or from an HTTP server--has already got the complete skill-set for getting and putting the same document to or from the hard-drive.

Note also that if the code was written as a relative path:

<xf:submission
 method="get" action="my-customer.xml"
/>

then the same XForm would work regardless of whether it was running on a server, or being loaded from a local disk.

Sending an email

Let's go further and see what new skills our author needs to acquire to send the same XML document by email--and whether we can save them the cost of buying a book on MAPI, and the time needed to read it:

<xf:submission
 method="put" action="mailto:john.doe@example.com?subject=Results"
/>

As before, no new API to learn. No need to roll your sleeves up and get to grips with MAPI so that you can write a powerful application that will allow a user to enter some XML data, validate it, and then send it via email.

The declarative approach

This 'hiding' of functionality is the key point. It's not that we've hidden a whole load of script behind a set of libraries or common functions--which of course it goes without saying that we have--but rather that in XForms we have a simple language that captures an enormous amount of generic functionality. But crucially, that generic language still allows us to use the functionality in very specific situations.

To put it the other way round, we still get specific functionality, such as saving files, sending emails, storing data in a database, and so on, but without having to learn lots of specific APIs. (And we haven't even begun to look at the other features this simple pattern captures, from validation through to different data serialisation options, which make this an even more powerful technique than we've seen so far.)