Recipe10.7.Populating a Form


Recipe 10.7. Populating a Form

Problem

You want to merge XML data into a predesigned form before delivering it to the client.

Solution

Provided you use XHTML or otherwise create well-formed HTML, you can use XSLT to merge an HTML document with data in an XML document. Here we will merge data in an XML document with a boilerplate HTML form. For example, imagine that your company's web designer created a form used when online customers are ready to complete a purchase. The form needs to add sales tax, based on the state in the U.S. selected by the customers, when entering their billing addresses. Since the states your company must collect tax for and the tax rates themselves can change, hardcoding these rates into the form would not be a good idea. Instead, you could have the server merge the tax data into the form dynamically by using XSLT.

The sales tax data might be stored (or extracted from a database) like this:

<salesTax>   <state>     <name>AL</name>     <tax>4</tax>   </state>   <state>     <name>AK</name>     <tax>0</tax>   </state>   <state>     <name>AZ</name>     <tax>5.6</tax>   </state>     ...       <state>     <name>WY</name>     <tax>4</tax>   </state> </salesTax>

The boilerplate HTML form might look like this:

<html>   <head>    <title>Check Out</title>    <script type="text/javascript" language="JavaScript">    <!--    /* Initialize tax for default state */    setTax(document.customerInfo.billState)                /*Recompute tax when state changes */      function setTax(field)         {       var value = new String(field.value)        var commaPos = value.indexOf(",")        var taxObj = document.customerInfo.tax       var tax = value.substr(commaPos + 1)       var subtotalObj = document.customerInfo.subtotal       taxObj.value = tax       document.customerInfo.total.value =            parseFloat(subtotalObj.value) +            (parseFloat(subtotalObj.value) * parseFloat(tax) / 100.00)      }    -->    </script>   </head>     <body bgcolor="#FFFFFF" text="#000000"> <h1>Check Out</h1> <form name="customerInfo" method="post" action="">   <table width="70%" border="0" cellspacing="3" cellpadding="3">     <tr>        <td width="7%">&#xa0;</td>       <td width="32%">          <div align="center"><b>Shipping Address</b></div>       </td>       <td width="20%">&#xa0;</td>       <td width="7%">&#xa0;</td>       <td width="34%">          <div align="center"><b>Billing Address</b></div>       </td>     </tr>     <tr>        <td width="7%">Name</td>       <td width="32%">          <input type="text" name="shipName" maxlength="40" size="50" border="0"         align="absmiddle"/>       </td>       <td width="20%">&#xa0;</td>       <td width="7%">Name</td>       <td width="34%">          <input type="text" name="billName" maxlength="40" size="50" border="0"          align="absmiddle"/>       </td>     </tr>     <tr>        <td width="7%">Address</td>       <td width="32%">          <input type="text" name="shipAddr" maxlength="40" size="50" border="0"          align="absmiddle"/>       </td>       <td width="20%">&#xa0;</td>       <td width="7%">Address</td>       <td width="34%">          <input type="text" name="billAddr" maxlength="40" size="50" border="0"         align="absmiddle"/>       </td>     </tr>     <tr>        <td width="7%">City</td>       <td width="32%">          <input type="text" name="shipCity" maxlength="40" size="50" border="0"          align="absmiddle"/>       </td>       <td width="20%">&#xa0;</td>       <td width="7%">City</td>       <td width="34%">          <input type="text" name="billCity" maxlength="40" size="50" border="0"         align="absmiddle"/>       </td>     </tr>     <tr>        <td width="7%">State</td>       <td width="32%">          <select name="shipState" size="1" align="absmiddle">         </select>       </td>       <td width="20%">&#xa0;</td>       <td width="7%">State</td>       <td width="34%">          <select name="billState" size="1" align="absmiddle" onChange="setTax(this)">         </select>       </td>     </tr>     <tr>        <td width="7%">Zip</td>       <td width="32%">          <input type="text" name="shipZip" maxlength="10" size="15" border="0"         align="absmiddle"/>       </td>       <td width="20%">&#xa0;</td>       <td width="7%">Zip</td>       <td width="34%">          <input type="text" name="billZip" maxlength="10" size="15" border="0"          align="absmiddle"/>       </td>     </tr>     <tr>        <td width="7%">&#xa0;</td>       <td width="32%">&#xa0;</td>       <td width="20%">&#xa0;</td>       <td width="7%">&#xa0;</td>       <td width="34%">&#xa0;</td>     </tr>     <tr>        <td width="7%">&#xa0;</td>       <td width="32%">&#xa0;</td>       <td width="20%">&#xa0;</td>       <td width="7%">Subtotal</td>       <td width="34%">         <input type="text" name="subtotal" readonly="1" value="100.00"/>       </td>     </tr>     <tr>        <td width="7%">&#xa0;</td>       <td width="32%">&#xa0;</td>       <td width="20%">&#xa0;</td>       <td width="7%">Tax</td>       <td width="34%">         <input type="text" name="tax" readonly="1"/>       </td>     </tr>     <tr>       <td width="7%">&#xa0;</td>       <td width="32%">&#xa0;</td>       <td width="20%">&#xa0;</td>       <td width="7%">Total</td>       <td width="34%">         <input type="text" name="total" readonly="1"/>       </td>     </tr>   </table> </form> </body> </html>

The transformation is a merge where the default action is to copy the contents from the boilerplate HTML to the output (see Recipe 6.5). When select elements are encountered for the bill-to and ship-to states, the state data is inserted as option elements. To keep the example relatively simple, I encoded the state name and tax rate into the option element's value attribute, but you might want to use a JavaScript-based lookup.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">      <xsl:import href="../util/copy.xslt"/>     <xsl:output method="html"/>      <xsl:template match="html">     <xsl:copy>       <xsl:apply-templates/>     </xsl:copy>       </xsl:template>     <xsl:template match="select[@name='shipState' or @name='billState']">     <xsl:copy>       <xsl:copy-of select="@*"/>       <xsl:for-each select="document('salesTax.xml')/salesTax/state">         <option value="{name}',',{tax}">           <xsl:value-of select="name"/>         </option>       </xsl:for-each>     </xsl:copy>  </xsl:template>      </xsl:stylesheet>

Discussion

Clearly technologies other than XSLT (ASP and JSP come to mind) might be better suited for the particular example covered in this solution. However, two aspects of the example might lead you to favor an XSLT-based solution.

First, notice that the boilerplate HTML contains standard HTML; there is no embedded server-side or client-side scripting. There is a bit of client-side scripting, but that could be inserted by the XSLT transformation. The author of the boilerplate HTML needs to know nothing about the technology or methodology that will be used to populate the form when it is loaded from the server. In fact, the web page author need not have any programming skills whatsoever. Likewise, the programmer of the transformation need not have any HTML or graphic design skills.[1] Between them, they merely need to agree on content and naming conventions. Thus, a transformation-based solution to dynamic content provides true separation of concerns that is lacking in other techniques.

[1] This sentence pretty much describes me.

Second, a transformational approach can do far more than simply inject content; it can also subtract and rearrange existing content without polluting the HTML with foreign gobbledygook.

See Also

Time and space did not permit me to cover XForms in this book, but readers interested in this topic should definitely investigate this new technology (http://www.w3.org/MarkUp/Forms/). The W3C describes XForms as follows:

The current design of Web forms doesn't separate the purpose from the presentation of a form. XForms, in contrast, are comprised of separate sections that describe what the form does, and how the form looks. This allows for flexible presentation options, including classic XHTML forms, to be attached to an XML form definition.

Key goals of XForms:

  • Support for handheld, television, and desktop browsers, plus printers and scanners

  • Richer user interface to meet the needs of business, consumer, and device control applications

  • Decoupled data, logic, and presentation

  • Improved internationalization

  • Support for structured form data

  • Advanced forms logic

  • Multiple forms per page, and pages per form

  • Suspend and resume support

  • Seamless integration with other XML tag sets




XSLT Cookbook
XSLT Cookbook: Solutions and Examples for XML and XSLT Developers, 2nd Edition
ISBN: 0596009747
EAN: 2147483647
Year: 2003
Pages: 208
Authors: Sal Mangano

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net