Menu

Getting Started with XForms

December 30, 2003

Bob DuCharme

The XForms standard, which became a W3C Recommendation last month, lets us define forms that are much more sophisticated than those of HTML. Perhaps more importantly, it makes it easier for applications that we write to grab and use the data entered into forms, because an XForms client can plug the data directly into any XML structure that you like.

Micah Dubinko's article "What Are XForms?" describes the history that led to the development of XForms and the processing model that provides a framework for its use. His more recent article "Ten Favorite XForms Engines" describes some available XForms implementations. In this article, I'm going to focus on creating some simple XForms, using them with one of those implementations and seeing the result.

Creating an XML Instance with a Form

Anyone familiar with XHTML (or for that matter, HTML) will recognize the structure and most elements in the following document. The bolded parts add a working XForms form to the document. It should work with any XForms-compliant processor with minimal changes; I tested it with the University of Helsinki's free, open-source X-Smiles browser.



<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">

  <head>

    

    <style type="text/css">

      h1 {font-size: 12pt }

      xf|input.itemClass { width:100px }

      xf|input.custNumClass { width:100px }

      xf|input.dateClass { width:80px }

    </style>

    <title>Enter Order</title>



    <xf:model id="model1">



      <xf:instance>

        <order xmlns="" itemNum="">

          <quantity/>

          <custNum/>

          <orderDate/>

        </order>

      </xf:instance>



      <xf:submission id="s01" method="put"

                     action="file:///temp/ex1order.xml" /> 



      <xf:submission id="s02" method="post"

                     action="http://xformstest.org/cgi-bin/showinstance.sh" /> 

    </xf:model>

  </head>



  <body>



    <h1>Enter Order</h1>

    <p>Enter data for order below.</p>



    <xf:input model="model1" class="itemClass" ref="@itemNum">

      <xf:label>Item Number</xf:label>

    </xf:input>



    <xf:input model="model1" ref="quantity">

      <xf:label>Quantity</xf:label>

    </xf:input>



    <xf:input model="model1" ref="custNum" class="custNumClass">

      <xf:label>Customer Number</xf:label>

    </xf:input>



    <xf:input model="model1" ref="orderDate" class="dateClass">

      <xf:label>Order Date</xf:label>

    </xf:input>



    <br />



    <xf:submit submission="s01">

      <xf:label>Save</xf:label>

    </xf:submit>



    <p>Click "Save" to store entered order.</p>



  </body>

</html>

The two new namespace declarations allow the use of elements from the XML Events and XForms namespaces. Our first example uses elements from the latter namespace to create two new sections in the document to add a form to it: an XForms model added to the XHTML's head element and a presentation section added to the body that specifies the form's controls and text.

The Model

The model section's two most important jobs are to specify the structure of the XML data that a form creates and what happens to the data when the form gets submitted. The model in the document above, which has an id value of "model1", has its structure defined in an xf:instance element that contains a small order XML element as a child to provide a template. Instead of storing a sample document between the xf:instance tags as shown, an empty xf:instance element can use a src attribute to point to an external file that holds a template document. The model can also identify a W3C XML Schema file with further information about constraints to apply to the data being created.

Note how the order element's empty xmlns attribute value sets the element to belong to no specific namespace. For my simple example, this lets me reference the order element and its components elsewhere in this document without specifying a namespace. If you want the instance created by the form to belong to a specific namespace, you would declare the namespace in the html start-tag with a namespace prefix and use that prefix each time you wanted to identify an element's membership in that namespace.

The model's xf:submission element can have many attributes, but the most important are action, which holds a URI indicating where to send the instance data, and method, which names the protocol for submission. The example above offers a choice of two xs:submission elements. The first is the simplest: it puts the instance in a file named ex1order.xml in a subdirectory of the root named "temp". (If this is not a convenient place for you, change this attribute value before loading this document.)

In a production application, a more common submission action is the posting of the document to a process that uses the data for something. The second xf:submission element demonstrates this by sending the instance data off to a script that Leigh Klotz wrote to echo back any XML sent to it. To send the instance data entered into this form to Leigh's script instead of to a file in a temp directory, change the "s01" in the xf:submission attribute near the bottom of the form to "s02".

The Display Form

The XForms features that let you describe a form's appearance resemble the ones used in HTML, with one crucial difference: they let you specify where in the model instance to plug in the entered value upon submission of the form. For example, if your form had an input field labeled "Last Name", you could plug it into an LName element in the model, a surname attribute, or whatever you like. In the example above, each xf:input element creates an input field on the form labeled with the string shown in its xf:label child element. The xf:instance element's model and ref attributes show where to put the data upon submission. They all specify a model of "model1", which is the only model defined in the document. The first xf:input element, which labels its field as an "Item Number," will put the entered value in the itemNum attribute of the model's order element. The second xf:input element puts the entered value in the quantity child of the model's order element, and so forth.

The first xf:input attribute is class, which means the same thing that it means in any HTML document: it lets you reference the element from a CSS stylesheet. The style element in the document's head element includes an entry specifying the appearance of xf:input elements (note the stylesheet's use of the pipe symbol "|" instead of a colon to separate the namespace prefix from the rest of the name) that have their class attribute set to "itemClass". The stylesheet sets their width at 100 pixels. It could also set the font-family, font-size, and other familiar CSS properties; I wanted to demonstrate the setting of the "width" property because this is how you set the width of XForms input elements -- despite other similarities to HTML's input element, XForm's input elements have no width attribute.

In addition to the input element used for simple text fields in this example, XForms offers many other input controls, such as pick lists, radio buttons, sliding range controls, and hierarchical tree selection controls, letting you create a form much richer than any pure HTML form.

The last special XForms element in the document is xf:submit. This displays a button with the specified label and, when clicked, submits the data entered into the form according to the instructions in the submission element whose id value is specified in the xf:submit element's submission attribute. In the example above, the entered data will be put into the file:///temp/ex1order.xml file, because the xf:submit element's submission attribute has a value of "s01". After loading the document into XSmiles .82 and filling out the form like this,

filled-out XForms form in XSmiles
Figure 1: Filled-out XForms form in XSmiles

clicking the "Save" button creates the following ex1order.xml file in the \temp directory:



<?xml version="1.0" encoding="ISO-8859-1"?>

<order itemNum="i30121" xmlns=""

		 xmlns:ev="http://www.w3.org/2001/xml-events"

       xmlns:xf="http://www.w3.org/2002/xforms">

          <quantity>3</quantity>

          <custNum>c99346</custNum>

          <orderDate>2003-11-24</orderDate>

        </order>

(The details of how data is saved to a disk file may vary from one XForms implementation to another.) XSmiles also displays the created instance upon submission of the form.

If the xf:submit element's submission attribute had a value of "s02" to point to the other xf:submission element, the entered data would be posted to the http://xformstest.org/cgi-bin/showinstance.sh script, which displays what it was sent under an "Instance Data" header:

showinstance.sh output of ex1order.xml
Figure 2: showinstance.sh output of ex1order.xml

For XForms development, a general purpose script like showinstance.sh is very handy. (A similar one at http://xformstest.org/cgi-bin/echo.sh shows additional information about the submitted instance.)

The showinstance.sh script is the simplest possible CGI for processing the submitted XML. A CGI process with an XML parser could pull individual field and attribute values out of the XML instance and use them for financial transactions, database interaction, publication events, or whatever you want. There's nothing new to server-side processing of XML; what makes XForms special is how easy it makes the client-side creation of XML to get shipped to these server applications.

Creating Repeated Elements

The XML instance that the example above creates is pretty short and simple. A more realistic scenario would allow someone doing data entry to add an arbitrary number of new order elements to the document being created. The following XHTML document does this by adding the bolded portions to the earlier example:



<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">

  <head>

    

    <style type="text/css">

      h1 {font-size: 12pt }

      xf|input.itemClass { width:100px }

      xf|input.custNumClass { width:100px }

      xf|input.dateClass { width:80px }

    </style>

    <title>Enter Order(s)</title>



    <xf:model id="model1">



      <xf:instance>

        <orders xmlns="">

          <order itemNum="">

            <quantity/>

            <custNum/>

            <orderDate/>

          </order>

        </orders>

      </xf:instance>



      <xf:submission id="s01" method="put"

                     action="file:///temp/ex2order.xml" /> 

    </xf:model>

  </head>



  <body>



    <h1>Enter Order(s)</h1>



    <p>Enter data for orders below.</p>



    <xf:repeat id="i1" nodeset="/orders/order">



      <xf:input class="itemClass" ref="@itemNum">

        <xf:label>Item Number</xf:label>

      </xf:input>



      <xf:input ref="quantity">

        <xf:label>Quantity</xf:label>

      </xf:input>



      <xf:input ref="custNum" class="custNumClass">

        <xf:label>Customer Number</xf:label>

      </xf:input>



      <xf:input ref="orderDate" class="dateClass">

        <xf:label>Order Date</xf:label>

      </xf:input>

      <br />

    </xf:repeat>



    <xf:trigger id="addOrderButton">

      <xf:label>Add Order</xf:label>

      <xf:insert nodeset="order" at="last()"

                 position="after"

                 ev:event="xforms-activate"/>

    </xf:trigger>



    <xf:submit submission="s01">

      <xf:label>Save</xf:label>

      <xf:hint>Click here to save new order.</xf:hint>

    </xf:submit>



    <p>Click "Save" to store entered orders.</p>



  </body>

</html>

<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">

  <head>

    

    <style type="text/css">

      h1 {font-size: 12pt }

      xf|input.itemClass { width:100px }

      xf|input.custNumClass { width:100px }

    </style>

    <title>Enter Order(s)</title>



    <xf:model id="model1">



      <xf:instance>

        <orders xmlns="">

(Additional minor changes make it clearer to the form's users that they can enter multiple orders at once.) The only difference to the model is the addition of an orders container element around the original order element to make sure that the created instance data has a single root element.

A much bigger change is the wrapping of an xf:repeat element around the xf:inputelements that make up the displayed form. This lets you bind the set of enclosed controls to each member of a collection of elements -- in this case, to a collection of order elements. Its nodeset attribute identifies the model node to repeat. (In this form, the model isn't explicitly identified like it was in the first example, but because the document has only one xf:model element, we don't need to.)

The xf:trigger element that follows the xf:repeat element is what lets the user enter multiple order elements. It has an xf:label child element to specify the text to display on the form's trigger button and an xf:insert element to specify the event to trigger.

This xf:insert element has four attributes. The nodeset attribute specifies what to insert: an order element. The at and position attributes specify where to insert the nodeset: after the last item in the collection being created. The last attribute in the element, ev:event, has the value "xforms-activate" to specify that a notification event is to take place. The value "xforms-activate" was removed from the XForms spec because of its redundancy with the "DOMActivate" event, but it seemed to work better than DOMActivate in the release of XSmiles that I used.

When you first load this document, XSmiles displays this:

XSmiles initial display of ex2order.xml
Figure 3: XSmiles initial display of ex2order.xml

You could fill out the information for that one order, but instead I clicked "Add Order" twice:

XSmiles  display of ex2order.xml after adding two order rows
Figure 4: XSmiles display of ex2order.xml after adding two order rows

XSmiles uses yellow highlighting to show which order is being filled in. You can click any displayed field to set its record as the one being filled in, and you can do all the data entry and edits you want in any order before clicking the "Save" button. After filling in some sample data for the three orders and clicking the "Save" button, XSmiles created this XML instance for me:



<?xml version="1.0" encoding="ISO-8859-1"?>

<orders xmlns="" xmlns:ev="http://www.w3.org/2001/xml-events"

        xmlns:xf="http://www.w3.org/2002/xforms">

          <order itemNum="i132">

            <quantity>1</quantity>

            <custNum>c3004</custNum>

            <orderDate>2003-11-23</orderDate>

          </order><order itemNum="i449">

            <quantity>3</quantity>

            <custNum>c1432</custNum>

            <orderDate>2003-11-23</orderDate>

          </order><order itemNum="i241">

            <quantity>2</quantity>

            <custNum>c1559</custNum>

            <orderDate>2003-11-24</orderDate>

          </order>

        </orders>

These two XForms applications have only scratched the surface of what you can do, but they do show you enough to get started:

  • How to specify the model of the instance you want to create
  • How to create a form for the user to input data
  • How to control your form's appearance using CSS
  • How to insert the data from that form into the model
  • How to save the created XML instance to a disk file
  • How to ship the created instance to a server process identified by a URL
  • How to have a form add an an arbitrary number of repeating elements, based on the model, to the created instance

For more information on the progress of XForms and its potential in XML development, see the articles Interactive Web Services with XForms and XForms and Microsoft InfoPath . Also see Micah Dubinko's book XForms Essentials, an introduction and practical guide to the specification.