Skip to main content

skip to main content

developerWorks  >  XML  >

XForms in Firefox

Learn by doing

developerWorks
Document options

Document options requiring JavaScript are not displayed


Rate this page

Help us improve this content


Level: Introductory

Elliotte Rusty Harold (elharo@metalab.unc.edu), Adjunct Professor, Polytechnic University

23 Jan 2007

Using the experimental Mozilla XForms extension, you can process XForms in your browser today. While not yet deployed widely enough for use on the public Internet, XForms may be suitable for some intranet applications. This article demonstrates basic XForms processing as currently supported by Firefox and the Mozilla XForms plug-in.

XForms makes development of Web-deployed applications faster and easier. XForms' clean architecture makes applications more robust, more scalable, faster, and more secure. Except for one little detail, developing with XForms would be a no-brainer. That detail is that no current browsers actually support XForms out of the box. Needless to say, this severely limits what you can do with XForms and where you can deploy them.

However, there are workarounds. Browser plug-ins exist for both Windows® Internet Explorer® and Firefox that add XForms support to these market-leading browsers. XForms processors have also been written in Flash that can be deployed to any browser with a Flash runtime. Finally, there are server-side solutions that precompile all XForms markup to classic Hypertext Markup Language (HTML) and JavaScript programs.

These solutions all have something to recommend them, but for first learning XForms the simplicity of support right in the browser really helps. You can write a piece of a form and then preview it. Then you can change it a little bit more and preview it again. If the form doesn't look quite right, tweak it a bit and reload. Server-side solutions like Chiba are good for deployment, but for learning nothing beats the rapid development cycle of a browser. Therefore, in this article I focus on using the Mozilla XForms plug-in in Firefox.

Installing the plug-in

The current version of the Mozilla XForms plug-in as I write this is 0.7.0.1, and it works in Firefox 1.5 and later. Installing the plug-in is easy. Just open Firefox, go to https://addons.mozilla.org/firefox/824/, and click the big green Install now button shown in Figure 1:


Figure 1. Install Mozilla XForms plug-in
Install now for MacOSX (425 KB)

This opens the dialog shown in Figure 2. The plug-in is not currently signed, but I expect it's as safe as any beta software. (It did crash my browser a couple of times when I loaded malformed content, which makes it vulnerable to denial of service attacks, so I'm hesitant to recommend it for full production use.) The goal is for this code to eventually become a standard part of all Firefox and Mozilla downloads, though that outcome is not a sure thing.


Figure 2. Install Mozilla XForms dialog
A Web site is requesting permission to install the following item: Mozilla XForms 0.7 Unsigned

You need to restart Firefox before the plug-in will take effect, but that's all there is to it. You're now ready to write and preview your first form built in XForms.



Back to top


Hello XForms

To make sure everything is installed and running correctly, I'll begin with a simple Hello World example that doesn't even talk to a server. It merely copies data from one field to another. The Extensible Hypertext Markup Language (XHTML) document containing this form is shown in Listing 1.

Listing 1. An XHTML document containing a simple XForm

<html xmlns="http://www.w3.org/1999/xhtml" 
    xmlns:xforms="http://www.w3.org/2002/xforms">
   <head>
    <title>Hello XForms!</title>
    <xforms:model>
     <xforms:instance xmlns="">
      <Name/>
     </xforms:instance>
    </xforms:model>
   </head>
   <body>
    <xforms:input ref="/Name">
      <xforms:label>Type your name in the box and press tab: </xforms:label>
    </xforms:input>
    <hr />
    <xforms:output value="concat('Hello ', /Name)">
      <xforms:label>Output: </xforms:label>
    </xforms:output>
   </body>
</html>

Save this code in a file named hello.xhtml (not hello.html). Open the file in Firefox. You should be able to type into the input field, press the Tab key, and then see the result shown in Figure 3. (You don't have to type Tab as long as you put the focus somewhere other than the input field.)


Figure 3. Hello XForms
Hello Elliotte Rusty Harold

You can also put the file on a Web server and load it from a URL. However, the server must send the document with the media type application/xhtml+xml, not text/html. Otherwise Firefox won't recognize the XForm.

This form has three parts: the model, the input, and the output. More complex forms will have quite a few more parts, including multiple inputs and outputs, but this is a good place to start.

The model is simply an Extensible Markup Language (XML) element that all the various inputs and outputs can reference using XPath expressions. The ref attribute of the input element specifies what part of the model that input will store data in. Here the single input stores data in the Name element.

The output element shows the text of its value attribute. This value attribute uses the XPath concat function to combine the literal string 'Hello' with the current value of the Name element in the model. You can place any XPath expression you like here, including ones that do arithmetic and other basic calculations. You do not need a round trip to the server for each and every operation.



Back to top


Choosing the data to collect

When designing a new XForm, you usually begin by asking what data you want to collect rather than what it will look like. This article shows an example that collects credit card information. You might see a form like this on any number of e-commerce sites. The form collects the following information:

  • The name as it appears on the credit card, a string
  • The card number, a thirteen- or sixteen-digit string with a check digit
  • The CVV2 or card identification number (four digits for American Express, and three digits for Visa, MasterCard, and Discover Card)
  • The expiration date, a month and a year
  • Address line 1, a string
  • Address line 2, a string
  • City, a string
  • State, one of over fifty enumerated two-letter strings (However, I've limited myself to just four states to keep the example shorter.)
  • Zip code, a five- or ten-character string consisting of digits and a hyphen
  • Credit card type (an enumerated list of four strings)

I've assigned types to every piece of information. This is important for validation. You can handle most (though not quite all) of this with an XML schema.

HTML forms usually submit data in an x-www-formurlencoded query string. XForms also use x-www-formurlencoded for safe GET requests that are passed as part of the URL. This is a limited format consisting only of name=value pairs. However, for POST and PUT requests, XForms submit a full XML document instead. The schema for this document is up to you. You can make it look like whatever is convenient for you to process on the other end. For instance, it could be an Atom document, a SOAP document, or just a format you custom design for the data you need to collect. For this example, a simple flat list suffices, as demonstrated in Listing 2:

Listing 2. An XML document containing (fake) credit card data

<CreditCardInfo>
  <Name>Elliotte Rusty Harold</Name>
  <Number>5123 4567 8901 2345</Number>
  <CVV2>314</CVV2>
  <Expiration>2007-01</Expiration>
  <Address1>6 Metrotech Center</Address1>
  <Address2>Dept. of Computer Science</Address2>
  <City>Brooklyn</City>
  <State>NY</State>
  <Zip>11201</Zip>
</CreditCardInfo>

A schema can validate this document on both the client and server side. Listing 3 is one possible schema.

Listing 3. A W3C schema for credit card data

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <xs:element name="CreditCardInfo">
    <xs:complexType>
      <xs:sequence>
          <xs:element name="Name" type="xs:normalizedString"/>
          <xs:element name="Number" type="CardNumber"/>
          <xs:element name="CVV2" type="CVV2"/>
          <xs:element name="Expiration" type="xs:gYearMonth"/>
          <xs:element name="Address1" type="xs:normalizedString"/>
          <xs:element name="Address2" type="xs:normalizedString" minOccurs="0"/>
          <xs:element name="City" type="xs:normalizedString"/>
          <xs:element name="State" type="State"/>
          <xs:element name="Zip" type="ZipCode"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>

  <xs:simpleType name="CardNumber">
    <xs:restriction base="xs:string">
      <!-- 13 or 16 digits with optional spaces -->             
      <xs:pattern value="(\d\d\d\d ?)(\d\d\d\d ?)(\d\d\d\d ?)(\d(\d\d\d)?)"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="ZipCode">
    <xs:restriction base="xs:string">             
      <xs:pattern value="\d\d\d\d\d(-\d\d\d\d)?"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="CVV2">
    <xs:restriction base="xs:string">             
      <xs:pattern value="\d\d\d\d?"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="State">
    <xs:restriction base="xs:token">
      <xs:enumeration value="AA"/>
      <xs:enumeration value="AK"/>
      <xs:enumeration value="AZ"/>
      <xs:enumeration value="NY"/>
      <!-- other states follow... -->
    </xs:restriction>
  </xs:simpleType>

</xs:schema>

Firefox will not submit invalid data. (Annoyingly, it does not provide any useful feedback to the users telling them that it did not submit the invalid data, what field contained invalid data, or how they can fix it.) However, servers should still verify validity for themselves. Servers that trust clients to send them only valid data are just begging to be hacked.

Finally, there are many constraints and rules the schema doesn't check. Most obviously, it doesn't check that the credit card is authorized for the purchase. It also doesn't verify the credit card's check digit. You will always need to write some code to verify constraints in a traditional programming language.



Back to top


The model

Now that you've decided what data to collect, you need to make a model of it. The model is just a sample instance document, with the form input data removed but all the markup and static text in place. Listing 4 demonstrates. It consists of an XForms model element that contains two pieces, the instance and the submission. The instance contains the sample instance document in #Listing 2, except that the actual data has been removed. That will be filled in from form input. What remains is the structure.

The submission element specifies the URL the data will be sent to and the method, GET or POST, by which it will be sent.

The model can also reference a schema for the input document in its schema attribute. However, this is not required; and, for the time being, Firefox ignores this, anyway.

Listing 4. An XForms model for credit card data

<xforms:model schema="cc.xsd">
  <xforms:instance>
    <CreditCardInfo xmlns="">
      <Name />
      <Number />
      <CVV2 />
      <Expiration />
      <Address1 />
      <Address2 />
      <City />
      <State />
      <Zip />
    </CreditCardInfo>
  </xforms:instance>
  <xforms:submission action="http://example.com/xform-processor" 
                     method="post" id="submit" />
</xforms:model>

The model is placed in the head of the XHTML document containing the form, next to the title, meta tags, and anything else that belongs in the head.



Back to top


The form

As in HTML forms, input is the basic element. Each input control must have a label that tells the users what they're expected to type into the box. This is provided by a label child element of the input element. For example, here's a simple input field for the name:

<xforms:input ref="Name">
  <xforms:label>Credit Card Number:</xforms:label>
</xforms:input></xforms:label>

You can put an input element anywhere in the body that you'd put any other inline or block element, such as strong or p. Unlike HTML forms, there is no master form element that contains all the input fields. They can be freely mixed into the HTML wherever it's convenient.

You also need a submit control that sends the form to the URL specified by the submission element in the model. This is just a submit element with a label:

<xforms:submit submission="submit">
  <xforms:label>Buy Now!</xforms:label>
</xforms:submit>

Listing 5 shows the code.

Listing 5. XForms input fields for credit card data

<xforms:input ref="Name">
  <xforms:label>Name as it appears on the credit card:</xforms:label>
</xforms:input>

<xforms:input ref="Number">
  <xforms:label>Credit Card Number:</xforms:label>
</xforms:input>

<xforms:input ref="CVV2">
  <xforms:label>CVV2:</xforms:label>
</xforms:input>

<xforms:input ref="Expiration">
  <xforms:label>Expiration date:</xforms:label>
</xforms:input>

<xforms:input ref="Address1">
  <xforms:label>Address Line 1:</xforms:label>
</xforms:input>

<xforms:input ref="Address2">
  <xforms:label>Address Line 2:</xforms:label>
</xforms:input>

<xforms:input ref="City">
  <xforms:label>City:</xforms:label>
</xforms:input>

<xforms:input ref="State">
  <xforms:label>State:</xforms:label>
</xforms:input>

<xforms:input ref="Zip">
  <xforms:label>Zip code:</xforms:label>
</xforms:input>

<xforms:submit submission="submit">
  <xforms:label>Buy Now!</xforms:label>
</xforms:submit>

Figure 4 shows the resulting form in Firefox.


Figure 4. The credit card input form
input form

This form is still quite ugly, but I'll pretty it up with a Cascading Style Sheets (CSS) stylesheet shortly.



Back to top


Other controls

The input control means "Get a text string from the user." However, there are other generic operations that are frequently used in forms. In particular, XForms defines ten generic form controls, each indicated by a different element:

  • Enter a line of text: input
  • Submit the form for processing: submit
  • Show a non-editable, calculated value to the user: output
  • Select one item from a list: select1
  • Select zero or more items from a list: select
  • Get more than one line of text from the user: textarea
  • Enter a line of text without displaying it to the user: secret
  • Choose a file: upload
  • Choose from a range of values: range
  • Launch a script: trigger

Most controls are represented by unique widgets. For instance, Firefox uses a button for a trigger, a slider for a range, and a standard file open dialog box for an upload control. A secret control is just a text field, exactly like an input control, except that what the user types is shown as bullets on the screen. Different browsers can show something different as long as they let the user enter the same basic type of data.

For example, one browser might show a select1 control as a pop-up menu or combo box while another might use radio buttons and a third a scrolling list. A fourth might choose between the different possibilities, depending on how many items are in the list. The form doesn't say what the choice looks like. It only says that the browser somehow presents the choice to the end users and allows them to pick one item from that list.

Besides the three controls that I've already demonstrated, the control that's really appropriate for the example at hand is select1. This control fits the state input perfectly. I don't wish to allow the user to enter any text, only to select one state from a list. Individual choices are item children of the select1 element, as Listing 6 demonstrates.

Listing 6. A select1 control that lists the states

<xforms:select1 ref="State">
  <xforms:label>State:</xforms:label>
    <xforms:item>
      <xforms:label>AA</xforms:label>
      <xforms:value>AA</xforms:value> 
    </xforms:item>
    <xforms:item>
      <xforms:label>AK</xforms:label>
      <xforms:value>AK</xforms:value> 
    </xforms:item>
    <xforms:item>
      <xforms:label>CA</xforms:label>
      <xforms:value>CA</xforms:value> 
    </xforms:item>
    <xforms:item>
      <xforms:label>NY</xforms:label>
      <xforms:value>NY</xforms:value> 
    </xforms:item>
    <!-- more states follow -->
</xforms:select1>

Figure 5 shows what this control looks like in Firefox.


Figure 5. A select1 control for the states
select1

Each item has a label and a value. In this example they're the same, though they don't have to be. The label is shown to the end user. The value is sent to the server when the form is submitted. For instance, you might want to localize the labels but keep the values the same when translating a site. Or, you could easily show full state names to the user but only send abbreviations to the server, like so:

    <xforms:item>
      <xforms:label>Alabama</xforms:label>
      <xforms:value>AA</xforms:value> 
    </xforms:item>



Back to top


Control appearances

XForms doesn't really say anything about what the input controls look like. In Figure 4, they're just classic graphical user interface (GUI) text fields. In another environment they could appear quite different, as long as they allow the user to enter a line of text. However, some controls need to be more constrained than simply a line of text. For example, the expiration date field might only accept an actual date in a particular format. XForms implementations are allowed (though not required) to take advantage of knowledge about the expected type of the data to present a different widget for the same control. For example, if Firefox knows that a date is expected, it can show the user a calendar control like the one in Figure 6 instead of the usual text field.


Figure 6. An XForms calendar control
Expiration date: calendar

Firefox will read the instance's schema to figure out which kinds of controls to offer. However, even if a document doesn't have a schema, there are several ways to bind a particular type to a particular control. The simplest is to add an xsi:type attribute to the control element. For example,

<xforms:input ref="Expiration" xsi:type="xs:date"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <xforms:label>Expiration date:</xforms:label>
</xforms:input>

Alternately, you can place the xsi:type attribute on the relevant element in the model, as shown in Listing 7. In this case, it applies to all controls that bind to this element.

Listing 7. The expiration element has type date

<xforms:model
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xforms:instance>
    <CreditCardInfo xmlns="">
      <Name />
      <Number />
      <CVV2 />
      <Expiration xsi:type='xs:date'/>
      <Address1 />
      <Address2 />
      <City />
      <State />
      <Zip />
    </CreditCardInfo>
  </xforms:instance>
  <xforms:submission action="http://example.com/xform-processor" 
       method="post" id="submit" includenamespaceprefixes=""/>
</xforms:model>

Finally, you can add an xforms:bind element in the model, as shown in Listing 8:

Listing 8. Binding the expiration element to type date

<xforms:model xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xforms:instance>
    <CreditCardInfo xmlns="">
      <Name />
      <Number />
      <CVV2 />
      <Expiration />
      <Address1 />
      <Address2 />
      <City />
      <State />
      <Zip />
    </CreditCardInfo>
  </xforms:instance>
  <xforms:bind  nodeset="Expiration" type="xs:date" />
  <xforms:submission action="http://example.com/xform-processor" 
       method="post" id="submit" includenamespaceprefixes=""/>
</xforms:model>

The advantage to binding in this manner is that the bind element can use a more complex XPath expression in its nodeset attribute to bind several inputs at once.

Whichever technique you use, Firefox displays a control designed specifically for that type if it has one. If it doesn't, then it uses a generic text field. The goal is to allow browsers to implement more complex functionality if they like, without breaking the form in less advanced browsers.



Back to top


Style

Up until now, the forms have all been pretty ugly. You can clean them up with CSS and HTML. Listing 9 is a simple CSS stylesheet that changes the font for the labels and aligns the form fields more neatly.

.xf-value

The .xf-value selector used here styles the input field value but not its label. This is actually inconsistent with the current CSS3 draft. The example really should use the ::value pseudo-class instead like so:

input::value { width: 20em; }
#ccnumber::value { width: 18em }
#zip::value { width: 12em }
#state::value { width: 3em  }

However, Firefox doesn't yet support this syntax.

Listing 9. A CSS stylesheet for the credit card form

@namespace xforms url("http://www.w3.org/2002/xforms");

xforms|label {
   font-family: Helvetica, Geneva, Lucida, sans-serif;
   font-weight: bold;
   width: 32ex;
   text-align: right;
   padding-right: 1em;
   padding-bottom: 0.8ex;
}

xforms|input, xforms|select1, xforms|submit {
   display: table-row;
}

xforms|input xforms|label, xforms|select1 xforms|label  {
   display: table-cell; 
}

.xf-value { width: 20em; }

#ccnumber .xf-value { width: 18em }
#zip      .xf-value { width: 12em }
#state    .xf-value { width: 3em  }

/* color invalid data red */
*:invalid  {
   background-color: red;
}

Figure 7 shows the resulting page.


Figure 7. The styled form
Expiration date: calendar

Figure 7 still isn't as pretty as the well-designed HTML forms you find on some Web sites today. However, that's purely a function of my own limitations. I'm a writer and a programmer, not a graphic designer. If you hand this form over to a good designer with some CSS experience, he or she could easily create a more attractive layout. In fact, it would be much easier for a designer to do that with XForms than with HTML forms because XForms don't have any predefined rendering semantics. Everything can be specified in CSS. Personally, I've never been able to make an HTML form look this good without using tables for layout. By contrast, I put this stylesheet together in about fifteen minutes. Imagine what a real designer could do.

This separation of concerns is a big advantage of XForms, XML, and generic markup in general. The designer can work on the layout and formatting without even touching the actual XHTML and XForms code. The page author can focus on the content without worrying about what it looks like. Each does what they're best at without stepping on each other's toes. The decoupling also means they can work at full speed without locking files or having to wait for each other.



Back to top


Limitations

The Mozilla XForms plug-in supports most of XForms 1.0 and all of the features discussed here. However, it is not yet complete. Some of the most significant current limitations include the following:

  • XForms can only be embedded in XHTML documents, which must be served as application/xhtml+xml. They can also be embedded in other kinds of XML documents. However, neither XForms inside HTML documents nor XHTML documents served as text/html are supported.
  • Forms can only be submitted back to the server they were originally downloaded from. You cannot submit forms to an arbitrary server.
  • Range controls are buggy and often don't work.
  • Firefox doesn't yet support the CSS3 pseudo-elements ::value, ::repeat-item, and ::repeat-index. You have to use the Mozilla-specific extensions xf-value, xf-repeat-item, and xf-repeat-index instead. You can use both if you like, and then your form should look decent in both Mozilla and non-Mozilla browsers.
  • Validation error messages are extremely poor, almost nonexistent really. Supplying a schema is just likely to confuse users since the form will fail silently without any indication of what they did wrong. Validation should be kept on the server for the time being.

By the time version 1.0 is released, the plug-in should have full support for XForms 1.0 and possibly CSS3.



Back to top


Summary

XForms is not only a more powerful means of designing and laying out forms than classic HTML forms; it's an easier one too. Because content is separated from presentation, CSS can be used to full effect. Furthermore, you can put the form elements anywhere on the page you like, intermixed with any markup. Finally, form tricks that require lots of JavaScript code, such as updating one field when the user enters data into another, are a trivial amount of declarative code in XForms.

I've only begun to examine the power of XForms in this article. Hopefully, this is sufficient to get you started. XForms is different from classic HTML forms, and there is a learning curve. That learning curve is not steep, though, and the easiest way to learn is by writing and testing forms directly inside a Web browser. Firefox with the Mozilla XForms plug-in is free and easy to use.

Client-side XForms processing won't be possible for public-facing sites until XForms is more widely deployed in browsers. However, that doesn't mean you can't deploy it on your intranet today. If you're already using Firefox (and if you aren't, you should be), all that's required is a simple plug-in. After that's installed, you can take full advantage of XForms' power, speed, and flexibility.



Resources

Learn

Get products and technologies
  • The Mozilla XForms Project wrote the plug-in described in this article. They include documentation of current deviations from the spec on their Web site. The deviations should be fixed before 1.0.

  • IBM trial software: Build your next development project with trial software available for download directly from developerWorks.

  • Get MozzIE, an open source control that allows you to render XForms in IE.

Discuss


About the author

Photo of Elliot Rusty Harold

Elliotte Rusty Harold is originally from New Orleans, to which he returns periodically in search of a decent bowl of gumbo. However, he resides in the Prospect Heights neighborhood of Brooklyn with his wife Beth, their dog Shayna and cats Charm and Marjorie. He's an adjunct professor of computer science at Polytechnic University, where he teaches Java and object-oriented programming. His Cafe au Lait Web site has become one of the most popular independent Java sites on the Internet, and his spin-off site, Cafe con Leche, has become one of the most popular XML sites. His most recent book is Java I/O, 2nd edition. He's currently working on the XOM API for processing XML, the Jaxen XPath engine, and the Jester test coverage tool. He'll be talking about Web Forms 2.0 at the SD West 2007 conference in Santa Clara in March.




Rate this page


Please take a moment to complete this form to help us better serve you.



 


 


Not
useful
Extremely
useful
 


Share this....

digg Digg this story del.icio.us del.icio.us Slashdot Slashdot it!



Back to top