Hack 76. Use Declarative Markup Instead of Script via XForms


When scripting gets too burdensome, capture a web application's intent in markup.

JavaScript code tends to get complex in deployed Ajax applications. The essential problem is that encoding the intent of a web application requires a large amount of procedural script, often with branches for different levels of browser support. For example, if your intent is to dynamically add a section to the page to capture additional user input, the script gets bogged down in low-level details of Document Object Model manipulation and the like. A cleaner approach uses higher-level markup to capture the intent, leaving the interpretation of that intent to the client.

The World Wide Web Consortium (W3C)the same folks who brought us HTML and XMLconsidered the things most commonly done by script, and agreed upon declarative ways to accomplish the same things. The result, called XForms, quite simply provides a vocabulary for authors to specify what they want to happen, instead of the usual approach of spelling out every tiny detail. Such a higher-level approach has additional benefits:

  • Nonbrowser devicessuch as phones and Interactive Voice Response systemscan easily work off the same design.

  • Web search engines have an easier time processing markup than interpreting script.

  • It's possible to interpret the markup on a server, allowing deployment across many different browsers.

  • XForms is especially adept at handling XML data.

Consuming and Producing XML

The last bullet above bears further discussion. In many situations that call for Ajax, the most convenient format for data is XML. (Other times, it's notsee "Receive Data in JSON Format" [Hack #7] for a discussion of JSON, an alternate format.) XForms is designed to work from a piece of XML instance data, which provides initial data values from the outside world, keeps track of any user changes, and can submit the data back out. Instance data makes for a convenient "scratch pad" within which to store temporary client-side state.

Here's an example that maintains a syndication feed for a podcast. I recently asked a podcaster to show me how he updates his feed, and he proceeded to use a copy of Windows Notepad and FTP. Manual editing, however, is error-prone, due to the intricacies of how the RSS format, iTunes extensions, and XML namespaces all combine (to the chagrin of podcasters everywhere).

See http://en.wikipedia.org/wiki/RSS_%28file_format%29 for more details on RSS.


An automatic fill-in-the-blanks solution, which opens the RSS, allows changes, and then writes the changes back, provides a better experience for both the content producer and the consumers.

For this hack, I'm using an XForms engine called FormFaces, implemented entirely in JavaScript. In accord with good design principles, it uses an HTTP GET to obtain the initial XML and an HTTP PUT to write it back. Because many servers don't yet work well with PUT, I included a simple PHP script to process the request:

<?php /* put.php */ /* PUT data comes in on the stdin stream */ $putdata = fopen("php://stdin", "r"); $fp = fopen("results.xml", "w"); while ($data = fread($putdata, 1024))        fwrite($fp, $data); fclose($fp); fclose($putdata); ?>

The prior PHP script captures the PUT data and writes it to a file named results.xml in the same directory. For actual deployment, more sophisticated security arrangements is desirable.

Normally, this hack would modify an existing RSS file, but what about for someone just starting out? The usual approach in this situation is to produce a skeleton XML file that contains all the structure of the desired output format, populated with dummy values. Here's an example of that for podcast XML:

<!-- rss.xml --> <rss xmlns:itunes="http://www.itunes.com/DTDs/Podcast-1.0.dtd" version="2.0"> <channel> <title>Podcast title here</title> <link>http://</link> <description>Description here</description> <language>en-us</language> <copyright>Copyright notice here</copyright> <itunes:image>http://</itunes:image>   <itunes:link rel="image" type="image/jpeg" href="http://">Description </itunes:link> <itunes:owner>   <itunes:name>Name here</itunes:name>  </itunes:owner>   <itunes:author>Name here</itunes:author>   <managingEditor>Contact info here</managingEditor>   <generator>Powered by XForms</generator>  <category>Audio Blog</category>   <image>  <url>http://</url>  <title>Image Title here</title>   <link>http://</link>  <width>300</width>  <height>300</height> </image>   <itunes:explicit>clean</itunes:explicit>   <itunes:subtitle>Show Subtitle here</itunes:subtitle>   <itunes:summary>Show Summary here</itunes:summary>   <itunes:category text="Audio Blogs" />   <lastBuildDate>Sun, 1 Jan 2006 10:00:00 PST</lastBuildDate>   <pubDate>Sun, 1 Jan 2006 10:00:00 PST</pubDate>   <item>  <title>Item Title here</title>   <description>Item Description here</description>   <pubDate>Sun, 1 Jan 2006 10:00:00 PST</pubDate>   <enclosure url="http://" length="1" type="audio/mpeg" />   <itunes:duration>1:00</itunes:duration>   <itunes:author>Author here</itunes:author>  </item>   </channel>  </rss>  

Given XML structured like that in the prior code sample, XForms markup can then create a complete environment in which to view and edit the file. A few aspects about the structure, though, need to be accounted for. For one, data repetition is rampant in the format, especially in the case where a single person is running the entire podcast. Our solution should not make us enter identical data over and over again. Secondly, note that the item block toward the end will repeat as many times as there are syndicated shows. As the number of shows grows, both the user interface and the resulting RSS need to grow.

The next code sample shows the complete HTML file, which should be placed in the same directory as the files listed earlier. It also includes a reference to the FormFaces script, available from http://www.formfaces.com. Other than the implementation script, no JavaScript is needed for this example. In more complicated situations, some script might need to be combined with XForms, but even so, the result will generally be shorter and more straightforward than with other approaches. Here's the HTML:

<!-- editrss.html --> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"       xmlns:xf="http://www.w3.org/2002/xforms"       xmlns:ev="http://www.w3.org/2001/xml-events"       xmlns:itunes="http://www.itunes.com/DTDs/Podcast-1.0.dtd"> <head>     <title>Edit iTunes RSS</title>     <link rel="stylesheet" type="text/css" href="xforms.css" />     <script type="text/javascript" src="/books/4/254/1/html/2/formfaces.js"></script> <xf:model>     <xf:instance src="/books/4/254/1/html/2/rss.xml"/>     <xf:submission  action="put.php" method="put" replace="none"/> </xf:model> </head> <body> <h2>Overall Info</h2> <xf:group ref="channel">     <xf:input ref="title"><xf:label>Title</xf:label></xf:input>     <xf:input ref="description"><xf:label>Description</xf:label></xf:input>     <xf:input ref="itunes:subtitle"><xf:label>Subtitle</xf:label></xf:input> <br/>     <xf:input ref="itunes:author"><xf:label>Author</xf:label></xf:input>     <xf:input ref="copyright"><xf:label>Copyright</xf:label></xf:input>     <xf:input ref="managingEditor"><xf:label>Contact</xf:label></xf:input>  </xf:group> <br/> <xf:trigger> <xf:label>Submit</xf:label>     <xf:send submission="s1" ev:event="DOMActivate"/> </xf:trigger> <hr/> <h2>Shows</h2> <xf:trigger> <xf:label>Add Show</xf:label>     <xf:action ev:event="DOMActivate">         <xf:insert nodeset="channel/item" at="1" position="before"/>         <xf:setvalue ref="channel/item[1]/itunes:author" value=                 "../../itunes:author"/>     </xf:action> </xf:trigger> <xf:trigger> <xf:label>Remove Highlighted Show</xf:label>     <xf:delete ev:event="DOMActivate" nodeset="channel/item" at=             "index('items')"/> </xf:trigger> <br/> <xf:repeat nodeset="channel/item" >  <xf:input ref="title">     <xf:label>Title</xf:label> </xf:input>     <xf:input ref="description"><xf:label>Description</xf:label></xf:input>     <xf:input ref="pubDate"><xf:label>Date</xf:label></xf:input> <br/>     <xf:input ref="enclosure/@url"><xf:label>MP3 URL</xf:label></xf:input>     <xf:input ref="enclosure/@length"><xf:label>Length</xf:label></xf:input>     <xf:input ref="itunes:duration"><xf:label>Duration</xf:label></xf:input>  <hr/> </xf:repeat> </body>  </html>

Figure 9-6 shows a screenshot of this application in action. After loading the page and editing the data, clicking the submit button PUTs the data back on the server.

Figure 9-6. Updating RSS content with XForms


Some things to note about the code:

  • XForms markup here appears in a different XML namespace, to ensure that the elements are uniquely distinguishable.

  • The short xf:model section provides the URL for both incoming and outgoing XML.

  • The ref attributes on the group and input elements are simple locators into the instance data. Full XPath syntax is available, as expressions like enclosure/@url show.

  • The xf:repeat element provides the needed repeating structure.

  • The value for itunes:author inside item is copied from the place where that piece of information is already specified. XForms includes a full spreadsheet-like system for resolving interdependencies on data relationships like these.

  • Note the elements named xf:trigger, which appear as buttons in most browsers. Even down to the names of controls, XForms enforces a high-level view of an application that is not based on a particular user-interface design.

  • Unlike conventional forms, XForms does not force a page churn during submission. This allows data to smoothly flow into the page, without worrying about details of XMLHttpRequest scripting.

As this example shows, XForms markup was not designed for terseness, but rather for comprehension. Even folks with no prior XForms experience should be able to look at an example like this and figure out what's going on. Of course, beyond what this short example shows, XForms has many more capabilities and conveniences. You can find out more about XForms in my book XForms Essentials, published by O'Reilly and available online under an open content license at http://www.xformsinstitute.com.

Hacking the Hack

This simple example edits only a few of the needed parts of a fully functioning syndication feed. Following the format established here, filling in the rest of the details is straightforward.

Besides FormFaces, other XForms engines have been written in JavaScript, not to mention Flash, Java, C++, and other languages. A good starting point for further XForms research is the article "Top 10 XForms Engines" at http://www.xml.com/pub/a/2005/02/09/xforms.html.

Micah Dubinko




Ajax Hacks
Ajax Hacks: Tips & Tools for Creating Responsive Web Sites
ISBN: 0596101694
EAN: 2147483647
Year: 2006
Pages: 138

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