Personal tools

Bindings in XForms

From PicoForms

Jump to: navigation, search

Contents

[edit] Bindings in XForms

This tutorial teaches the different kind of bindings and evaluation scopes that are used in XForms. It will also teach how to use XPath predicates to disable parts of the UI or individual controls.

[edit] Single Node Bindings

A single node binding is defined in XForms as an XPath node-set expression where the first node in document order is used. This means that having the following instance:

<dataset>
  <name>Joe Smith</name>
  <phone type="home">+1 876-1234</phone>
  <phone type="mobile">+1 987-5678</phone>
</dataset>

Then the XPath expression /dataset/phone will select both phone numbers and the first node rule will select the home phone number.

Many of the elements in XForms can have a single node binding which is expressed with the attribute ref. When nodes which has a single node binding is nested then the ancestor changes the behaviour of its descendence. E.g. take the following XForms UI:

<xf:group ref="/dataset">
  <xf:input ref="name">
    <xf:label>Name:</xf:label>
  </xf:input>
</xf:group>

The xf:group selects the root element of the instance. The xf:input is evaluated in the context of that element which means that name will select the first name child of dataset. The default evaluation context in XForms, e.g. the context when there is no ancestor element with a single node binding (or nodeset binding), is defined to be the root element of the default model's first instance. This means that the expression ref="name" will select the same name as the previous example.

[edit] Changing instance

The XForms model allows for more than one instance. The first instance is always the default context for the bindings. A XForms which has more than one instance must have IDs on the instances following the default instance (e.g. it is optional to have an ID on the default instance but when having more than one instance it is likely it will need an ID as well). XForms then defines an XPath function called instance() which is used to change the context from one instance to another. The argument to the instance function is a string and is used as an ID specifying which instance to use. The function returns the root element for the instance which is identified by the argument (or an empty node-set if the ID could not be located or failed to point to an ID). Take the following XForms model and let us see how this all works:

<xf:model>
  <xf:instance id="first">
    <data xmlns="">Instance 1</data>
  </xf:instance>
  <xf:instance id="second">
    <data xmlns="">
      <a>A</a>
      <b>B</b>
    </data>
  </xf:instance>
</xf:model>
<xf:group ref="instance('second')">
  <xf:input ref="a">
    <xf:label>Second instance element a:</xf:label>
  </xf:input>
  <xf:input ref="instance('default')">
    <xf:label>Default instance 'Instance 1':</xf:label>
  </xf:input>
</xf:group>
<xf:input ref="instance('second')/b">
  <xf:label>Second instance element b:</xf:label>
</xf:input>

Here the group will change the context for its descending controls to the second instance. The first input will be bound to the a element. The following input will be change to the default instance again. The input outside of the group just illustrates that the context can be changed in the XPath language, e.g. that instance('second') is just a common XPath function.

[edit] Disable Controls using Bindings

In XForms there are several ways to make a control or element in the user interface irrelevant. Here we will look at one way of doing this without effecting the state of the bound node.

The functionality is based upon the definition that a binding which does not point to a node should behave as irrelevant. This means that if we can make an XPath expression which does not point to a node when it should be irrelevant and bind to the node when relevant, then we can control the state depending on some data. To do this we use a predicate in the XPath expression. The following form is an example:

<xf:model>
  <xf:instance>
    <data xmlns="">
      <relevant>true</relevant>
      <bound>Node is bound</bound>
    </data>
  </xf:instance>
</xf:model>
<xf:input ref="bound[../relevant = 'true']">
  <xf:label>bound when relevant is true</xf:label>
</xf:input>

Here the input is only bound to the instance when the node relevant is 'true'. Changing the relevant node to something else will make it irrelevant. This technich can be applied to many different scenatiors, e.g. testing if the value is greater that a specific value could be written as ref="self::node()[. > 5]" and it is possible to expression combinations or alternative using the XPath and and or operator.

[edit] Model Based Switchs

In XForms switch constructs like tabs are often expressed with xf:switch and xf:case. However it is possible to use the technique described in the previous section to do the functionality using xf:group. The reason one would do that is because the tabbing is then controlled from the data and not by the UI. It will be possible to test the state of the UI and to send the state of the UI to a server/back-end.

To make a group based switching (or model based switching) we will have an instance which controls the active switch. In the UI we will have some groups which are made mutually exclusive using predicates so only one can be relevant at a time. We can then change the selected group by changing the value in the instance. See the following for an complete example.

<xf:model>
  <xf:instance>
    <data xmlns="">
      <selected-switch>1</selected-switch>
    </data>
  </xf:instance>
</xf:model>
<xf:group ref="self::node()[selected-switch=1]">
  .. tab 1 ..
</xf:group>
<xf:group ref="self::node()[selected-switch=2]">
  .. tab 2 ..
</xf:group>
<xf:trigger>
  <xf:label>Tab 2</xf:label>
  <xf:setvalue ref="selected-switch">2</xf:setvalue>
</xf:trigger>
<xf:trigger>
  <xf:label>Next tab</xf:label>
  <xf:setvalue ref="selected-switch" value=".+1"/>
</xf:trigger>

The xf:triggers will change the selected group when activate. The first will always set it to be the second tab. The next trigger will increment the selected group and hence select the next one.

Used for preloading please ignore