XForms Modularization

From W3C XForms Group Wiki (Public)

Disclaimer: This page has been established as a forum for discussing creating modular extensions to XForms, but does not represent any formal endorsement of any proposal or direction.

Introduction

The XForms specification as it exists through 1.1 is built on the premise that an XForms document can be specified completely with the core XForms element set. This is a laudable and necessary goal. However, in developing XForms in the wild, there are quite frequently structures that occur with sufficient regularity - tables, linked lists, record enclosures, and even specific node bindings such as those for addresses - that providing some mechanism for reuse or extension may become both useful and desirable for XForms developers.

One solution to this is to define specific complex content extensions for such entities as tables, menus or linked lists. The downside to this approach is that the XForms specification is intended as an abstraction mechanism - it is up to the host environment to provide the specific presentation for the various controls. Moreover, this can lead into a morass of specialty components inhabiting the already fairly extensive XForms namespace, as well as projecting XForms as "yet another framework".

A second solution to this problem is to extend the XML Binding Language. The challenge to this approach is, first, that the XBL specification itself is very much in flux, especially as proposals by various actors within the W3C sphere and elsewhere see advantages to forking XBL to specifically accommodate web development, while there are currently few consisting working implementations of XBL on the web. Moreover, such a solution does not necessarily work well in contexts where an XBL framework does not exist, and it requires working outside of the XForms namespace.

As a consequence, the proposal that is made here is to create an extension mechanism that mirrors the very successful extension mechanisms that are currently in use by both XSLT and XQuery. In this approach, an XForms document would support an XForms Module consisting of a set of templates and functions. A template is a block of parameterizable XML (either a node or a node sequence) that would exist in a separate, user-declarable namespace, would be able to take a context, and would have a set of associated parameters. Such modules would be referenced externally as part of the underlying model, and new elements in this namespace and context would then be able to take XForms content and render it out as a distinct component, usable from within an XForms document. A function similarly is a block of parameterizable XML, but it incorporates imperative content that can be used to create an extension function for the internal XPath implementation within the associated namespace.

Modules would be imported into the XForms document with an <xforms:import> statement as part of the model that the extension functions and templates would call from. Because the modules are defining interfaces (and ultimately objects) such modules can also be associated directly with external language invocations, such as JavaScript components, Java classes or related entities, and it would become the responsibility of the XForms engine to parse and render the associated forms. This provides a pro-forma mechanism for establishing these interfaces for third party vendors, as well as provides a default XForms mechanism for implementing such interfaces by the user.


Example: A Simple List Module

This module contains one template, which will let an XForms developer create either a numbered or named list. It is contained in mywidgets.xfm

<xforms:module namespace="http://www.myWidgets.org/xmlns/">
    <xforms:template name="simplelist" type="node()">
        <xforms:param name="nodeset" select="." as="attribute"/>
        <xforms:param name="numbered" type="xs:boolean" select="fn:false()" as="attribute"/>
        <xforms:param name="class" type="xs:string" select="'property'" as="attribute"/>
        <xforms:choose>
            <xforms:when test="$numbered">
                <div class="{$class}" xmlns="http://www.w3.org/1999/xhtml" xmlns:xf="http://www.w3.org/2002/xforms">
                  <ol>
                     <xf:repeat nodeset="{$nodeset}">
                        <li><xf:output ref="."/></li>
                     </xf:repeat>
                  </ol>
                </div>
            </xforms:when>
            <xforms:otherwise>
                <div class="{$class}" 
                     xmlns="http://www.w3.org/1999/xhtml" 
                     xmlns:xf="http://www.w3.org/2002/xforms">
                  <ul>
                     <xf:repeat nodeset="{$nodeset}">
                        <li><xf:output ref="."/></li>
                     </xf:repeat>
                  </ul>
                </div>
            </xforms:otherwise>
        </xforms:choose>
    </xforms:template>
</xforms:module>          

The Xforms document would then reference this module via an import statement.

<xforms:model>
    <xf:import xmlns:widget="http://www.myWidgets.org/xmlns/" src="mywidget.xfm"/>
    <xforms:instance>
        <colors>
            <color>red</color>
            <color>green</color>
            <color>blue</color>
            <color>yellow</color>
            <color>cyan</color>
            <color>magenta</color>
            <color>white</color>
            <color>black</color>
        </colors>
    </xforms:instance>
</xforms:model>
<body>
   <widget:simplelist nodeset="colors" numbered="false()" class="property-list"/>
</body>

This would generate a bullet list of colors:

  • red
  • green
  • blue
  • yellow
  • cyan
  • magenta
  • white
  • black

Example: A Sorting List Module

A second template for a sorted list illustrates the use of element parameters:

<xforms:module namespace="http://www.myWidgets.org/xmlns/">
    ...
    <xforms:template name="sortlist" type="node()">
        <xforms:param name="nodeset" select="." as="attribute"/>
        <xforms:param name="numbered" type="xs:boolean" select="fn:false()" as="attribute"/>
        <xforms:param name="class" type="xs:string" select="'property'" as="attribute"/>
        <xforms:param name="sort-by" type="xs:string" select="." as="attribute"/>
        <xforms:param name="sort-direction" type="xs:string" select="'ascending'" as="attribute"/>
        <xforms:param name="item-display" type="xs:string" select="." as="element-selected"/> 
        <xforms:choose>
            <xforms:when test="$numbered">
                <div class="{$class}" xmlns="http://www.w3.org/1999/xhtml" xmlns:xf="http://www.w3.org/2002/xforms">
                  <ol>
                     <xf:repeat nodeset="{fn:sort($nodeset,$sort-by,$sort-direction)}">
                        <li><xf:output value="{$item-display}"/></li>
                     </xf:repeat>
                  </ol>
                </div>
            </xforms:when>
            <xforms:otherwise>
                <div class="{$class}" 
                     xmlns="http://www.w3.org/1999/xhtml" 
                     xmlns:xf="http://www.w3.org/2002/xforms">
                  <ul>
                     <xf:repeat nodeset="{fn:sort($nodeset,$sort-by,$sort-direction)}">
                        <li style="color:{.}"><xf:output value="{$item-display}"/></li>
                     </xf:repeat>
                  </ul>
                </div>
            </xforms:otherwise>
        </xforms:choose>
    </xforms:template>
</xforms:module>          

The sortlist component would then be invoked in a very similar manner, except for the inclusion of element properties:

<xforms:model>
    <xf:import xmlns:widget="http://www.myWidgets.org/xmlns/" src="mywidget.xfm"/>
    <xforms:instance>
        <colors>
            <color>red</color>
            <color>green</color>
            <color>blue</color>
            <color>yellow</color>
            <color>cyan</color>
            <color>magenta</color>
            <color>white</color>
            <color>black</color>
        </colors>
    </xforms:instance>
</xforms:model>
<body>
   <widget:sortlist nodeset="colors" numbered="false()" class="property-list" sort-by="." sort-order="descending">
       <widget:item-display select="fn:concat(fn:upper(substring(.,1,1),fn:lower(substring(.,2))"/>
   </widget:sortlist>
</body>

This would generate a sorted (and colored) bullet list of colors in title case:

  1. Blue
  2. Black
  3. Cyan
  4. Green
  5. Magenta
  6. Red
  7. White
  8. Yellow

Example: A Title Case Function

Finally, the complex title function could could be encapsulated into a function within the module, with the assumption that the language being evaluated is XQuery. A client solution might end up using JavaScript, but same principle applies:

<xforms:module namespace="http://www.myWidgets.org/xmlns/">
    ...
    <xforms:function name="title-case" type="xs:string">
        <xforms:param name="value" select="item()" as="attribute"/>
        <xforms:script type="xquery"><![CDATA[
fn:concat(fn:upper(fn:substring(fn:string($value),1,1),fn:lower(fn:substring(fn:string($value),2))
        ]]></xforms:script>
    </xforms:function>
</xforms:module>          

The function similarly would then be invoked within the appropriate namespace:

<xforms:model>
    <xf:import xmlns:widget="http://www.myWidgets.org/xmlns/" src="mywidget.xfm"/>
    <xforms:instance>
        <colors>
            <color>red</color>
            <color>green</color>
            <color>blue</color>
            <color>yellow</color>
            <color>cyan</color>
            <color>magenta</color>
            <color>white</color>
            <color>black</color>
        </colors>
    </xforms:instance>
</xforms:model>
<body>
   <widget:sortlist nodeset="colors" numbered="false()" class="property-list" sort-by="." sort-order="descending">
       <widget:item-display select="widget:title-case(.)"/>
   </widget:sortlist>
</body>

which would produce the same result as above.

The principle difference between a function and a template is that the latter produces nodal content (and makes use of attributes as parameters), while the former produces atomic content.

Formal Declarations

TBD

Summary

I believe that the result of this approach will be a more controlled modularization/extension mechanism than currently is the case for XBL extensions. It reduces the dependency upon any one scripting implementation (including XBL), can be used by XForms developers who don't need to have strong JavaScript background, and is in fact quite consistent with both XSLT and XQuery modularization efforts. It does require some innovations - inline attribute evaluation, for instance - but again these are consistent with existing XML display and query technologies.

It also opens up a much needed venue for XForms developers - the ability to create and extend XForms modules that can be utilized both by others within the same organization and those on the web in general. It makes interactive SVG forms much easier to develop, since they can be invoked parametrically and served up without the developer needing to know the details of SVG, and the approach makes it easier for browser vendors to differentiate their own XForms product while still adhering to the XForms standards.