XForms provides a number of action handlers that can add and remove nodes in the instance data, as well as change the values of nodes. It's also possible to gain access to the instance data via an XML DOM so that values can be changed through script, if necessary.
There are a number of ways to insert and delete nodes in XForms, based around the action handlers insert and delete.
insert allows a node to be inserted into a nodeset, at a certain position. The handler has a set of default behaviours defined which creates for a very powerful and flexible action.
All of the following examples assume this initial instance data:
<instanceData xmlns="">
<list>
<y>y1</y>
<y>y2</y>
<y>y3</y>
</list>
<templates>
<x>x1</x>
</templates>
</instanceData>
The simplest form of the handler specifies a nodelist, and duplicates the last item in the list:
<xf:insert nodeset="list/y" />
The result would be:
<list>
<y>y1</y>
<y>y2</y>
<y>y3</y>
<y>y3</y>
</list>
It is also possible to indicate a position to copy the last node in the list to. The at attribute indicates which node in the nodeset to place the copied node after:
<xf:insert nodeset="list/y" at="1" />
The result would be:
<list> <y>y1</y> <y>y3</y> <y>y2</y> <y>y3</y> </list>
To place the copied node before the node referred to by @at, use the position attribute with a value of before:
<xf:insert nodeset="list/y" at="1" position="before" />
The result would be:
<list> <y>y3</y> <y>y1</y> <y>y2</y> <y>y3</y> </list>
Note that the default value for @position is after, so the following are equivalent:
<xf:insert nodeset="list/y" at="1" /> <xf:insert nodeset="list/y" at="1" position="after" />
The @at attribute is actually an XPath expression, so it is possible to calculate an insert position at run-time. The expression is evaluated in the context of the first node in the @nodeset, as illustrated by the following example:
<xf:insert nodeset="list/y" at="count(../y) - 1" position="before" />
The result would be:
<list> <y>y1</y> <y>y3</y> <y>y2</y> <y>y3</y> </list>
It is also possible to copy a node from some other location than the last node in the list, by using the origin attribute. The node could be in the target nodelist:
<xf:insert nodeset="list/y" origin="list/y[1]" />
which would give:
<list>
<y>y1</y>
<y>y2</y>
<y>y3</y>
<y>y1</y>
</list>
But the node to copy need not be in the nodeset being updated, and could come from some other location, such as a collection of templates:
<xf:insert nodeset="list/y" origin="templates/x" />
The result would be:
<list>
<y>y1</y>
<y>y2</y>
<y>y3</y>
<x>x1</x>
</list>
If the nodeset referred to is empty then the node being copied is created as a child of the insert handler's context node (if you are not clear on the evaluation context then read this):
<xf:group ref="list">
<xf:repeat nodeset="y">
<xf:output ref="." />
</xf:repeat>
<xf:trigger>
<xf:label>Add new 'x'</xf:label>
<xf:action ev:event="DOMActivate">
<!--
The evaluation context here is still 'list', from
the xf:group, above.
-->
<xf:insert nodeset="y" origin="../templates/x" />
</xf:action>
</xf:trigger>
</xf:group>
In many situations there may be no natural 'context', such as the group in the previous example. In these circumstances we use the context attribute to set a context explicitly:
<xf:insert context="list" nodeset="y" origin="../templates/x" />
Note that the @origin value is also evaluated relative to the evaluation context.
XForms provides the getInstanceDocument method to allow access to instance data within a model. An example of its use is here.