ShowTable of Contents
Here's a sample form which demonstrates a technique for copying values from one set of elements into another set of elements.
When to use this sample
Before you use this sample, you should understand the case where this specific technique is applicable, and when a different technique should be used. This technique is only necessary if you have a set of source elements whose parent node name differs than the set of target element's parent node (see figure 1). If you have two sets of elements where the parent node names are identical (see figure 2), you should consider using an xforms insert action to simply make a copy of the source set of elements including the parent node. Here's a code snippet to illustrate the difference I'm talking about:
Figure 1. Different parent node names, the sample in this article is applicable. In this example, your goal would be to copy value1 into element1 in the outputMessage, and value2 into element2 in the outputMessage.
Figure 2. Same parent node names, you could use the sample in this article, but using xforms:insert is simpler
You should note that this logic will only work if you have equal an number of elements (cardinality) between the set of source elements and target elements, and both sets are in the same order. Also, another important thing to consider is that the target elements will be readonly (since they are continuously updated by the bind).
How the bind works
In this sample, the instance I made looks like this:
<xforms:instance id="INSTANCE" xmlns="">
As you may have guessed, I'm trying to copy the values in the set of elements in the source into the set of elements in the target. In order to do this, one straight forward way to do it would be to create a set of three binds, one for each sub element, or you could use a set of 3 setvalue actions if there was an appropriate event you could use to trigger the actions. But what if you had many more elements? Let's look at a way to do this with a single bind that handles the whole copy for all the elements.
calculate="../../source/*[position() = count(current()/preceding-sibling::*)+1]"
The nodeset of a bind always describes the elements you want to do the work on, in this case, the target elements. I've written an XPath statement with a wildcard (*) so that the nodeset contains ALL the elements that are children of the <
The expressive power of XForms binds comes from the fact that you can write a model item property or MIP (e.g. "calculate") so that it acts on each of the elements in the nodeset. That's what we're doing here, so you need to think about the XPath in the calculate MIP being evaluated for each individual node in the nodeset separately. Let's break down the parts of this XPath statement:
This takes us up two levels from the current element (any one of the children of <
), looking at our data instance, you should be able to see that this will land us at the root of the instance (<
Continuing on, we are selecting all the nodes that are children of the <
element, using the wildcard character (*).
[position() = count(current()/preceding-sibling::*)+1]
Now we get to the interesting part. This filter (a.k.a. XPath predicate, indicated with "") will act on the children of the <
element that we've just selected with the wildcard, in order to select the correct element that is to be used as the source for our copy operation. The position() method returns the position of the current context node (the source node). We compare that with the position of the target node, and to get that position number, we need to do something a little tricky. First of all, we need to get back the context of our target element we're working on in the bind, remember how I said the bind can do work on each element at a time? We need to get back the current target element that the bind is working on. To do that, we use the XForms method "current()". Once we have that, we can count all the preceding-siblings that came before it (then add one) to figure out its position. Comparing these two values, we end up simply copying the first element to the first element, the second element to the second, and so on down the line.