This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 2.5 License.
Hit 'T' to view with full notes. Download the presentation with examples
XForms is a standard developed by the W3C and designed to replace the tired, old form features of HTML that have served us as Web developers for so long. Designed from the ground up for maximum flexibility and browser/device independence, XForms bear almost no resemblence to the simple forms tags that you're probably used to working with in HTML 4 and XHTML 1 today.
The most fundamental difference with XForms is that the data that a form will submit is kept entirely separate from the user interface widgets like text input boxes, drop-down menus and checkboxes that you set up to let a user enter the data. And as we'll see, that one goal has had the biggest impact on how different it is to write XForms compared to old-style HTML forms.
But XForms brings a lot of other nice things to the table, too. It's designed so that you can write one form that will work on different kinds devices—from desktop Web browsers to teeny mobile phone screens—providing a user interface that is appropriate to each device automatically. It lets you declare potentially complex rules for data validity (what constitutes valid data to be submitted) and field interdependencies (if option 1 is selected in field A, show the user fields B, C and D).
...and what about a richer set of widgets? It's certainly the first thing that comes to mind when you think of the limitations of HTML forms. Wouldn't it be nice to have slider controls for range selection and calendar controls for date selection? XForms certainly allows for these things, but as we'll see these are more of a freeing side-effect of the way XForms is designed than a feature of XForms itself.
As of the latest working draft, the W3C has nominated XForms to totally replace standard HTML forms in XHTML 2.0, but you can just as easily use XForms in XHTML 1.0 documents. That's a good thing, because XHTML 2.0 support in browsers is still a long way off.
Before we dive into XForms, let's stop and review some of the issues with the existing HTML forms tags that XForms was designed to solve.
First of all, with HTML forms, everything you submit is a string. Now, at the end of the day Web browsers and Web servers all talk to each other with text, so form submissions will always be in the form of text, but HTML forms simply aren't aware of the different types of data that text may represent. Numbers, dates, times, even geographical locations... if you want to prompt the user for these kinds of things, you have to work within the HTML forms limitation of treating them all just like text strings.
Secondly—and this one may seem a little weird—HTML forms are tied to HTML. To get the user to fill in and submit an HTML form, you need to present that form in an HTML document. If your company gets bought out by Macromedia and your site design transitions to using Flash, all the work you put into finessing your HTML forms goes out the window—you have to reimplement everything in Flash forms. XForms, on the other hand, is independant of HTML. Today already there are all sorts of different platforms for presenting XForms to users for them to fill in data. You can certainly put XForms in HTML, but you can also put XForms in Flash, in Scalable Vector Graphics (SVG), or even in a dedicated XForms browser for intensive data entry. It would even be possible to present XForms as a series of voice prompts over the telephone. In short, XForms can go places that HTML can't.
Finally, HTML forms can't overlap. If you've got a table where you want each column to be a separate form that the user can submit, you simply can't do that with HTML. You need to glom all your forms together into one big form and sort out which fields are actually important on the server side. This is why frameworks like ASP.NET have taken to just calling Web pages "forms" and recommending that developers just put a
<form> tag directly inside the
As Web developers we've had plenty of time to get used to these kinds of limitations, but XForms addresses all of them. If anything, XForms is bewildering in the freedom it gives you.
The Mozilla XForms project came about after many, many requests for this standard to be supported by Mozilla browsers (including Firefox). Internet Explorer already has a number of 3rd party XForms plug-ins (most notably FormsPlayer), but if XForms were supported by Firefox, it would be available on all the major desktop operating systems, which would make it ripe for widespread deployment.
About a year ago now, Novell and IBM contributed paid developers to launch the Mozilla XForms project, an effort to implement the current XForms recommendation in Mozilla browsers. Novell recently dropped its support for bugetary reasons, but development continues on the IBM side with the help of several volunteers, the lead developer from Novell among them.
Firefox 1.5, which was just released last week, is the first Mozilla browser to support the various XML-related technologies required as a foundation for XForms. The XForms project has also released an early beta that runs on this browser as an extension. There are still significant portions of the recommendation to be implemented, but what's there is functional and very promising.
In fact, I'm going to use the current Mozilla XForms beta today to take you through the building of a simple but non-trivial form.
Let me try to give you a "big picture" view of what XForms looks like from a development perspective. I mentioned earlier that the most fundamental design decision at the heart of XForms is to separate the data being submitted from the widgets used to enter that data. So when you are writing an XForm, you are in fact writing two separate bodies of code: one that defines the model (the data to be submitted) and another that defines the user interface (the widgets for entering the data).
When XForms is used in an XHTML document, the model is an
<xforms:model> tag in the
<head> of the page. The diagram at right shows the full code listing for the example we're about to write, and the model is highlighted in red.
The user interface is made up of a whole bunch of XForms tags for form controls (widgets), which can be scattered all throughout the HTML document <body>. Since the layout for this example will be pretty simple, all of the widget tags (highlighted in green) come in an uninterrupted block, but that certainly need not be the case.
So let's start with the XForms Model. Instead of just submitting simple named fields and values like HTML forms, XForms lets you submit structured XML data from a form. In the model, you define the data structure—the tags that will make up the XML document to be submitted. You also populate this document with any default values you wish to present to the user of the form.
Also in the model, you set up your requirements for the data to be inserted into this document structure. You can indicate which data types each tag or attribute must contain, which tags must contain data before the form can be submitted (i.e., required fields), and any other constraints you want to set up, like the range of dates that is acceptable for a credit card expiry date. You can even set up interdependencies for the different data items in the document, like marking the credit card number and expiry date fields as only being relevant if the user has selected a credit card as the type of payment for a purchase.
Finally, the model lets you configure one or more ways to submit the data to the server. You can select not only the URL and HTTP method (GET, POST, etc.) to use, but also how to format the XML data for transmission, and what to do with the response that comes back from the server.
<xforms:bind nodeset="/login/username" required="true()"/>
<xforms:bind nodeset="/login/username" constraint="string-length(.) > 0"/>
<xforms:submission id="loginsubmit" action="submit.php" method="post"/>
So here's what a fairly simple XForms model looks like. Remember, this can sit right inside the
<head> of an XHTML document. This model will be used for someone to log into a website by providing his or her username, and an optional password. I've assigned this model an ID of "login".
The first thing in the model is the instance—the XML document structure to be submitted. In this case, the document will consist of a
<login> tag containing a
<username> tag and a
<password> tag. Both of these nested tags start out empty. But to log in, a user will have to provide at least a username. For the sake of this example, we'll make the password optional (i.e., a user can log in as a guest by simply providing a name).
The next part of the model is what makes that username a required value for submission. The
<xforms:bind> tag lets you add requirements to elements of your document. The first one here makes the username tag a required field. Anyone familiar with XPath will be able to understand the attribute values of this tag—XPath, a language for locating and identifying parts of an XML document for processing, is an integral part of the XForms standard.
<xforms:bind> tag sets up a constraint on the username element. You can use constraints to enforce all sorts of conditions on the data values that are submitted. In this case, we're simply requiring that the data value that is given be greater than zero characters in length. That's actually just a more complicated way of saying it's a required field, and the only reason I need it here is to work around a bug in the previous
<xforms:bind> in the current build of Mozilla XForms.
The last tag in the model describes a way of submitting this model. In this case it will be submitted via an HTTP POST request to
submit.php. You can actually set up multiple submission methods in a single model if you need to. The ID ("loginsubmit" in this case) is what lets you specify which method to use, as we'll see later.
With a model all ready to receive user input and be submitted to a server-side script, we must now turn our attention to building a user interface for our form.
When learning XForms for the first time, many people expect to find that it supports a plethora of rich widgets like calendars, sliders, spin buttons, spreadsheets and whatnot. In fact, the range of form controls to be found in the XForms standard is only slightly larger than those to be found in HTML forms. This is because XForms is designed so that it can run equally well on very simple devices and complex Web browsers.
Instead of cluttering the spec with a bunch of form controls that some devices may not be able to present, XForms instead allows each device to decide for itself what sort of widget to display for each form field, depending on the type of data to be input and the capabilities of the device. For example, a sophisticated Web browser might pop up a full calendar display for the user to input a date, but a mobile phone might instead just display a set of three text fields for the month, day, and year and simply restrict what numbers the user can type into these fields. As far as XForms goes, you're just creating an input field for an element in your model that happens to require a date value.
What XForms does add in the interface department is support for a whole lot more information and interactivity. You can provide a hint for each field of your form to help the user decide what to type into it, and display a message in response to any event. You can apply special styles to fields that are invalid (must be changed before submission), disabled (not relevant given the currently-entered form data), or read-only (not available for editing). You can allow the user to choose between a number of sub-forms.
Finally, XForms user interfaces are very easy to provide in multiple languages. All of the text labels can be loaded from an external XML file, which is automatically selected by the browser based on the user's locale, and which can be switched on the fly as well.
<xforms:input ref="/login/username" model="login"> <xforms:label>Username</xforms:label> </xforms:input> <xforms:secret ref="/login/password" model="login"> <xforms:label>Password</xforms:label> </xforms:secret> <xforms:submit submission="loginsubmit"> <xforms:label>Log In</xforms:label> </xforms:submit>
Here's a dead simple XForms user interface. We want to prompt the user for a username and password, and then let him or her submit the form. So we provide controls for each of those things.
The first tag is
<xforms:input>, which is bound with its attributes to the username element in the model with ID "login". Because that element has no special type assigned to it, it will expect a string value (the default), so the control that will be displayed is a standard text input field. The
<xforms:label> tag inside it creates a text label for the field.
The second tag is
<xforms:secret>, which is bound to the password element in the model. This tag works just like
<xforms:input>, except that it obscures the value that is typed into the field. Obviously, the control that will be displayed is a standard password input field.
Finally, we have an
<xforms:submit> tag, which displays a submit button that, when pushed, triggers the submission method with the specified ID. Recall that we defined this submission method in our model.
So far so good, but you'll agree this example is pretty basic. Let's take advantage of a few more of XForms's features to beef up the example a bit. Let's say the user is a new visitor to the site, and wishes to register for access. Let's create a second model for this purpose.
XForms lets you define more than one model in your page if you want to. Let's create a second model that will be used when the user is registering for a new account. We'll prompt the user for a desired user name, and email address, and one or more websites. For each website, we'll want a URL and a choice of displaying the website to other users on the site. For the website URL, we'll put in a default value of "http://". We'll also assume the user wants to keep the site private by default.
Since we want the
<public> tag to contain either true or false, we'll identify it as containing boolean data, instead of the default string data. XForms lets you do this a number of ways, depending on how much control you need. You can write a complete schema in XML Schema Definition Language (XSD) to describe the structure for the entire instance in detail (Mozilla XForms doesn't yet support this). You can can set up
<xforms:bind> tags to set the data type requirements of individual elements or attributes (Mozilla XForms's support for this is still a little buggy). Or you can apply XMLSchema attributes directly to the elements of the instance, which works perfectly in Mozilla XForms, but submits these attributes with the document, which may or may not be a problem for you.
Incidentally, XForms's use of XMLSchema to describe data types is one of the biggest barriers to entry for new developers and one of the biggest sticking points for browsers that might otherwise implement it. XMLSchema all on its own is an even more complex standard than XForms. Thankfully for novice XForms developers, you can pick and choose the features of XMLSchema that you need without getting lost in the language, and it still offers plenty of grunt when used this way (e.g. you can set up a regular expression that a value must match before it can be considered valid).
Now, we need to adjust our user interface to give the user the option of filling in and submitting this new user model instead of the login model. First, we'll put the current form controls inside an
<xforms:switch>, which will let us switch between displaying two different sets of form controls. The login form will be in a
<xforms:case> with ID "logincase", and we'll create a second case with ID "newusercase" for our new form controls.
In this second case, we'll need input fields for the username and email address, and we'll also want fields for the website URL and for the choice of whether to make the site public or not. Because the public field in the model is set up to require a boolean value (true or false), this input field will be displayed as a checkbox. And of course we'll need a submit button that points to the submission setup for our new model. That'll do the trick for the moment.
Now with the switch construct in place, the login form will be displayed by default. We need to provide a way for the user to switch to the new user signup form. To do this, we'll start with an XForms control group,
<xforms:group>. We'll put two
<xform:trigger> tags in this group: triggers are generally rendered as buttons in forms—they let the user trigger some action when clicking on them. We'll make each of these buttons display one of the two forms: the login form, or the user registration form. We do this by putting in an
<xforms:toggle> tag, which sets the currently-displayed case for a switch given its ID. We bind this action to the user clicking the button using the XML Events attribute
ev:event set to
DOMActivate, the name of the event that occurs when the button is clicked.
The last thing we need to add to our form is the ability to specify multiple websites when the user signs up. First, we need to modify the user interface to display a website URL field and public checkbox for each website in the model. We do this by wrapping an
<xforms:repeat> tag around the relevant fields.
Second, we need to provide a button that the user can click to add a new website to the model, which will display a new set of form controls in the form. Again, because we're providing a button for the user to trigger an action, we use a
<xforms:trigger> tag. When the user clicks this button, we want a number of things to happen. To make several things happen in response to an event, we wrap them in an
<xforms:action> tag. In this case, we want to insert a new website element into the model. For this, we use an
<xforms:insert> tag. Because XForms copies the values from the previous item in the model by default, we want to reset the newly-created website element to have the default values of "http://" and false.
To improve usability, we should also put the user's cursor into the newly-created website URL field. We'll create another
<xforms:action> to do this, because we need to allow the field creation from the previous action to update the user interface before we set the focus. To put the focus where we want it, we use a
<xforms:setindex> to set the current item of the repeating control group to the index of our newly-created fields. Then we use
<xforms:setfocus> to put the focus in the URL field of that item.
That should do it! Let's try out our beefed-up example...
I just wanted to take a moment to single out a few of the features of XForms that I think are really cool, but that for one reason or another I wasn't able to cover today.
As I mentioned, the actual widgets presented by an XForms-capable browser or device are largely left up to the browser or device to decide. That said, XForms does specify one form control that is definitely beyond what is currently supported by HTML forms: the range control. This control will allow the user to select a value within some range. The spec gives a slider as an example of how this control might be rendered, but depending on the data type this could also allow for spinner controls and range-limited calendars. Unfortunately, precisely because it would require a custom widget to be developed, the range control is not yet supported by Mozilla XForms—the project seems to be generally focusing on the more difficult implementation problems of the model and event systems of XForms before tackling the relatively easy (but eye-candy rich) issues of custom controls.
Finally, there is an alternative submission method called instance replacement. In our example, when we submitted the form the browser sent the model instance as a filled-in XML document to the server, and then displayed the response from the server as a new Web page. That's the default behaviour of XForms. It's possible instead to tell XForms to simply use the response from the server to replace the model instance in the current document, which would in turn update the form controls to display the data received from the server.
Using this, you can create a form that can edit a whole series of records on the server by submitting each updated record and receiving in response the next record to be edited. XForms's sophisticated event handling system can display status messages to confirm that the submitted records were successfully processed and saved even as a new record is displayed for editing. This can really speed up data entry applications, because instead of having to send a whole Web page back in response to each submission, the server can just send the next record as lean XML data.
This is actually the kind of application that people are using AJAX to build today, because the whole point of AJAX is that it allows you to make small, efficient server requests in the background to update the currently displayed page on the fly. As I've just described, XForms allows you to do this very same thing without having to deal with many of the headaches that come with practical AJAX development.
Of course, for XForms to fulfill its objective of providing a replacement for HTML forms, it will need to be supported on more than just Mozilla browsers. And to a limited extent, that is happening. Internet Explorer arguably has the best XForms support, with a number of plug-ins that add support for XForms to this browser. The most complete of these is FormsPlayer, which unfortunately is commercial, and unless you pay a licensing fee will add a bunch of intrusive branding to any page containing XForms. Novell XForms Explorer is a decent alternative that is freely available (though not open source), which actually does a better job at strictly conforming to the specification. Its support for the XForm spec, though extensive, is not quite complete. Additionally, the future of this project is in some doubt with Novell having pulled the plug on its support for Mozilla XForms.
In other browsers, XForms support is pretty much absent. So what are the options if you need the functionality of XForms cross-browser?
Well, one pretty amazing project called DENG has implemented the XForms standard in Flash. Not only that, but it's also got support for a lot of XHTML, CSS3, SVG and more. This open source project has been around for over three years and is surprisingly mature and stable, supporting a limited but definitely useful and usable subset of XForms.
XForms is a relatively robust specification, so ideally, unsupported features aside, any valid XForms code should run equally well on all of these implementations. In the real world, this isn't quite the case, but it's close. The biggest hurdle is applying CSS to your XForms controls to style them so they fit in with the visual design and layout of your site. The problem here is that the XForms specification relies on a number of features of CSS3 to mandate how XForms controls should be styled, and since support for CSS3 in browsers is all but nonexistent as yet, each implementation has devised its own solution to these problems. What this means is that for each XForms implementation that you want to support, you'll generally have to write a separate style sheet to style your form controls generated by that implementation.
This leads nicely into a discussion of some of the problems with XForms as a technology.
Furthermore, browsers need to implement support for these same technologies: XML namespaces, XML Schema, XPath, XML Events, CSS3. Mozilla is doing an admirable job of building support for these technologies, mainly because much of the browser itself is written with these same technologies. But for browsers that aren't, most of these technologies are big ask individually, let alone together.
Finally, XForms was developed at the height of the XML craze, and as such is burdened with a lot of unfashionable XML baggage. Today's developers want their frameworks to offer power through intelligent defaults—an approach often called convention over configuration. XForms, on the other hand, provides power through options: putting configuration over convention. What this means is that while XForms makes very complex applications relatively easy to implement, it requires a seemingly unreasonable amount of code to do very simple things.
So what do we do about these issues? Do we toss XForms in the scrap heap and start again?
As far as managing the complexity for developers, some good tools would certainly help. The makers of FormsPlayer, the commercial XForms plug-in for Internet Explorer, have a product called XFormation, which claims to make XForms easy. As soon as the open source implementation efforts produce some stable results, I'd expect a similar tool to come out of the open source arena.
Many developers have a problem with this for many reasons. First of all, the Web Forms 2.0 spec was written by one man: Ian Hickson from Google. Now Google does a lot of great things, but writing standards compliant code hasn't traditionally been one of them. Web Forms 2.0 doesn't use any of the XML language extension mechanisms established by the W3C to enable support for things like more advanced form controls to coexist with HTML code that can be validated and understood by systems that don't grok those extensions.
But the solution to the XForms dilemma may nevertheless come from Web Forms 2.0. It turns out that even though it requires invalid HTML markup to use, Web Forms 2.0 is a lot easier for browser makers to support, because it's not burdened with all those XML technology prerequisites. Both Safari and Opera are expected to support Web Forms 2.0 in their next major versions.