Someone wrote me recently asking how they could aggregate a number of form controls together to achieve a common purpose.
Sidebar: It is legal to use the word they as a singular pronoun. And not just when the answer to the question he/she or s/he asked is applicable to a plurality of people, it can be used at any time to avoid those horrible slashing language kludges. Still, I have to admit it's easier on the ear when the answer applies to many.
Of course, the answer to the above question applies to many! I'll use the syntax of pure XForms as a pseudo-code for your visual experience in the Workplace Forms designer, where you can add whatever presentational beautification seems appropriate. So, let's say you want to aggregate the input controls to collect the parts of a phone number. Since forms are often written per locale, I'll focus on the North American format, since the same idea applies to other formats. The format consists of a 3 digit area code, a 3 digit exchange and a 4 digit local. So your instance data would look something like this:
<xforms:instance xmlns="" id="X"> <phone> <areaCode/> <exchange/> <local/> </phone></xforms:instance>
Now you'd hook up input controls to allow the user to enter data for each of these parts. We'll talk about the elided part of each control in a moment:
<xforms:input ref="areaCode" incremental="true" id="areaCode"> <xforms:label>Area Code</xforms:label> ...</xforms:input>
<xforms:input ref="exchange" incremental="true" id="exchange"> <xforms:label>Exchange:</xforms:label> ...</xforms:input>
<xforms:input ref="local" incremental="true" id="local"> <xforms:label>Local:</xforms:label> ...</xforms:input>
The automatic tabbing feature begins with the
incremental attribute. By default, an
xforms:input allows the user to type any number of characters without changing the XML data to which the control is bound until the user indicates that the value should be committed (by hitting tab or changing the input focus). This is the
false setting for
incremental, which is the default. But if you set it to
true, then the bound XML instance data node is changed on every keystroke.
Every time you change the data associated with an XForms form control, the control receives an
xforms-value-changed event. This means that an XForms action can be created to listen for that event and perform a behavior when it happens. Like, say for example, change the focus if the user types 3 digits. In XForms 1.1, you will be able to write the area code input as follows:
<xforms:input ref="areaCode" incremental="true" id="areaCode"> <xforms:label>Area Code</xforms:label> <xforms:setfocus ev:event="xforms-value-changed" control="exchange" if="string-length(.)=3 and . >= 0"/></xforms:input>
ev:event attribute says that the
xforms:setfocus action is a handler for the
xforms-value-changed event. The form control to which the focus will be sent is the one with
id="exchange", and this will occur only if the current context node contains three characters and evaluates to a number greater than or equal to zero. The current node is of course
areaCode, and in XPath you signify the current node using a period, which is very familiar to many because it is the same notation as is used for the current directory on a file system.
OK, all of this is simple enough, but why did I use the future tense. The reason is that XForms 1.1 is a working draft. XForms 1.0 is the recommendation, so what can you do now. Well, for all of its elements, XForms 1.0 defines the "Common Attributes" bundle as including any foreign namespaced attributes. This was done to allow host languages (like XFDL for Workplace Forms) to be able to add their own enhancements in advance of the recommendations to meet pressing business needs and also to experiment with the best ways of adding new features to the XForms language. In this case, the need for conditional execution of an action is clearly needed, and it's pretty much a done deal that the
if attribute will be in the XForms 1.1 when it becomes a recommendation. So, in Workplace Forms you can use the conditional attribute today, only it has to be in the XFDL namespace, so you would simply adjust the
xforms:setfocus handler as follows:
<xforms:input ref="areaCode" incremental="true" id="areaCode"> <xforms:label>Area Code</xforms:label> <xforms:setfocus ev:event="xforms-value-changed" control="exchange" xfdl:if="string-length(.)=3 and . >= 0"/></xforms:input>
That's all folks!