I had a problem with updating instance structure which contains repeating nodes. I wanted to use <action while=""/> construction but there was a problem using defined iterator inside this loop. Eventually it always used one value (first one) even though it was incremented. I resolved this problem by using xxforms:evaluate function thus I have:
xxforms:evaluate(concat('instance(''main'')/item[',xxforms:bind('idx'),']'))
instead of simpler
instance('main')/item[xxforms:bind('idx')]
Is this the only way to iterate across the list of nodes inside an action?
Example:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:xf="http://www.w3.org/2002/xforms"
xmlns:ev="http://www.w3.org/2001/xml-events"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xxforms="http://orbeon.org/oxf/xml/xforms">
<head>
<title>Test</title>
<xf:model id="model">
<xf:instance id="main" xmlns="">
<main>
<item>
<name />
</item>
<item>
<name />
</item>
<item>
<name />
</item>
</main>
</xf:instance>
<xf:instance id="temp" xmlns="">
<main>
<idx></idx>
<value>inserted node</value>
</main>
</xf:instance>
<xf:bind id="idx" nodeset="instance('temp')/idx" type="xsd:integer" />
</xf:model>
</head>
<body>
<xf:trigger>
<xf:label>Not working as expected</xf:label>
<xf:action ev:event="DOMActivate">
<xf:setvalue bind="idx" value="1" />
<xf:action while="number(xxforms:bind('idx')) le count(instance('main')/item)">
<xf:insert context="instance('main')/item[xxforms:bind('idx')]" nodeset="name" position="after" origin="instance('temp')/value" if="not(exists(value))" />
<xf:setvalue bind="idx" value=". + 1" />
</xf:action>
</xf:action>
</xf:trigger>
<xf:trigger>
<xf:label>Working as expected but too complicated</xf:label>
<xf:action ev:event="DOMActivate">
<xf:setvalue bind="idx" value="1" />
<xf:action while="number(xxforms:bind('idx')) le count(instance('main')/item)">
<xf:insert context="xxforms:evaluate(concat('instance(''main'')/item[',xxforms:bind('idx'),']'))" nodeset="name" position="after" origin="instance('temp')/value" if="not(exists(value))" />
<xf:setvalue bind="idx" value=". + 1" />
</xf:action>
</xf:action>
</xf:trigger>
<widget:xforms-instance-inspector id="orbeon-xforms-inspector" xmlns:widget="http://orbeon.org/oxf/xml/widget" />
</body>
</html>
So I get as a result (first trigger):
<main>
<item>
<name/>
<value>inserted node</value>
</item>
<item>
<name/>
</item>
<item>
<name/>
</item>
</main>
but expected (second trigger):
<main>
<item>
<name/>
<value>inserted node</value>
</item>
<item>
<name/>
<value>inserted node</value>
</item>
<item>
<name/>
<value>inserted node</value>
</item>
</main>
Here is a version that works:
The issue is that
xxforms:bind('idx')returns an untyped value, even through you specifiedxsd:integer. XForms currently doesn’t specify that type annotations on binds must cause a typed value to be provided (see these notes on type annotation). This means that in this case, the predicate value is not a number (XPath has both boolean and numeric predicates, and this is often a source of confusion). In order to make it a numeric predicate, converting to a number is needed.Here I use
xs:integerasnumberis kind of an XPath 1 legacy function, and it returns anxs:doublewhile thecount()function returns anxs:integer).There is much simpler solution with
xxforms:iterate:iterateis currently an extension, but XForms 2 will add it.