Tuesday, May 8, 2012

More XForms bindings

Photo by preservationgal
In a previous post, we covered the basics of controls bindings, where XPath expressions are used to bind a control to XML elements. But it's not always that simple, is it? So now is the time to cover all the cases you can encounter with binding expressions.

First, there is another difference with HTML worth pointing to: if the XML node does not exist, the control becomes non-functional (non-relevant), and appears hidden (or in some implementations grayed out). So it's important to create an instance (XML document) which contains all the elements and attributes your controls are pointing to, unless you want to hide them on purpose.

So now we have covered binding expressions pointing to a single element, or pointing to nothing. Are there other possibilities? Well yes!

1. Attributes

Obviously, a control can bind to an XML attribute too:
<xforms:input ref="@tag">
Here the input field reads and writes to a "tag" attribute:

2. Multiple nodes

What if the XPath expression returns more than one node? For example, assuming the document from the previous post, the following would return two elements:
<xforms:input ref="instance('zoo')/animal/nickname">
XForms solves this by saying that you just take the first node returned. Except, that is, for bindings that support multiple nodes. The only control which supports this is <xforms:repeat> (but other XForms constructs support multiple nodes: <xforms:itemset><xforms:bind><xforms:header>, and the <xforms:insert> and<xforms:delete> actions.

3. Atomic values

What if the binding expression returns something other than a node, like a number? In XForms 1.1, this was disallowed and the binding would throw an error. XForms 2 changes this because XPath 2 deals more generally with items, which can be nodes but also atomic values. Also, and maybe more importantly, there are some interesting use cases to bind to atomic values, in particular with <xforms:repeat> and <xforms:itemset>.

So XForms 2 says that it's ok to bind to atomic values. For example this will work:
<xforms:input ref="42">
However, the input control appears read-only, because in XPath it doesn't make sense to write to an atomic value.

In general, it's not very useful to bind controls such as <xforms:input> to atomic values. It's ok for <xforms:output>, although that control already had a value attribute for that purpose. Where this really shines is for repeats or itemsets:
<xforms:repeat ref="1 to 10">
  <xforms:output value="."/><hr/>
Then there are a couple of  things you probably shouldn't do:

1. Binding to text nodes
<xforms:input ref="instance('zoo')/animal/nickname/text()">
This is allowed but discouraged. Why? First, there might not be a text node, and then the control won't show. Second, if there is one, and the user enters a blank string, the text node will go away (because XForms and other specifications like the XPath data model say you can't have empty text nodes), and the control will hide. Not very useful!

2. Comment nodes and processing instruction nodes
<xforms:input ref="comment()">
<xforms:input ref="processing-instruction()">
XForms doesn't prevent you from doing this, but not all implementations might support it, and XForms doesn't say what must happen. Since recently, Orbeon Forms allows you do do that, and the control will read and write the comment text or the processing instruction value. It's unlikely you will need that, and it's not guaranteed to be portable between implementations, but at least XForms allows it and if you need it you'll be a happy camper!

Finally, there are things that are outright prohibited:

1. Binding to elements that contain other elements
<xforms:input ref="instance('zoo')/animal">
This is not allowed. Why? Probably because there are a few ways this could be understood. For example, the control could read and write text at the beginning of the element, or at the end. Or, it could read the XPath string value, but overwrite everything when writing back! Also, XML Schema doesn't validate values in such elements with so-called complex content. To make things clearer, XForms says there that if you do that, the binding will throw an error.

2. Binding to a document node
<xforms:input ref="instance('zoo')/..">
<xforms:input ref="instance('zoo')/root()">
This is not allowed. Why? Because in XML it's not possible to have non-blank character data at the top of an XML document, so the control wouldn't be able to write data there. Here again, the binding will throw an error.

In both these prohibited cases, Orbeon Forms now uses its new error handling behavior.

Please let us know if we missed any aspects of control bindings!