I am having problems filtering items using Orbeon XForms. The situation is that I have a checkbox bound to an instance, the instance is defined as:
<xf:instance id="Include-model">
<data>
<value type="xs:string">true</value>
</data>
</xf:instance>
and the checkbox is declared as:
<xf:select ref="instance('Include-model')/value" selection="closed" appearance="full" >
<xf:item>
<xf:label>Include all</xf:label>
<xf:value>true</xf:value>
</xf:item>
</xf:select>
So the checkbox is initially checked.
Now I have a list of items in another instance defined as:
<xf:instance id="items-model">
<Items>
<Item>
<value>1</value>
<status>Show</status>
</Item>
<Item>
<value>2</value>
<status>Show</status>
</Item>
<Item>
<value>3</value>
<status>Hide</status>
</Item>
</Items>
</xf:instance>
and an associated bind:
<xforms:bind id="items-bind" nodeset="instance('items-model')Items/Item">
These items are displayed in a repeater
<xforms:repeat bind="items-bind" appearance="xxforms:internal">
.....
What I need is to to be able to filter the items based on the state of the checkbox. If it's checked then the bind should include all items, if it's unchecked the bind should contain only the items which have 'Show' as a values if their status element.
Please help, and save me what little hair I have left.
TIA
First, let's get rid of a few issues:
<xforms:bind id="items-bind" nodeset="instance('items-model')Items/Item">
is not a correct XPath expression. Use instead:
<xforms:bind id="items-bind" nodeset="instance('items-model')/Item">
This points to all items. That's because instance('items-model')
already points to the root element of the instance, so instance('items-model')
points to the Items
element.
A second, minor thing: you probably don't want appearance="xxforms:internal"
on the repeat. That is an extension used to tell the XForms engine not to generate HTML markup for a given XForms control. It is not supported on xforms:repeat
but it's better to to clutter the code with it anyway.
A third thing, also minor: you probably don't need the type="xs:string"
annotation, as by default values are considered as strings.
Finally, I would not use ids ending with -model
for instances. I would use -instance
instead. Another minor thing, but it can be a bit confusing. So let's call them 'main-instance' and 'items-instance' instead.
This said, the key is to write an XPath expression to filter the items. Now one issue is that your bind points to all items. So if you refer to your bind with the bind
attribute, which just refers to the bind by id, you can't filter.
One solution is to use the Orbeon extension function xxf:bind()
which allows you to refer to binds from XPath expressions:
xxf:bind('items-bind')[instance('main-instance')/value = 'true' or status = 'Show']
Your repeat then becomes:
<xf:repeat ref="xxf:bind('items-bind')[instance('main-instance')/value = 'true' or status = 'Show']">
Here is a complete example that works:
<xh:html xmlns:xh="http://www.w3.org/1999/xhtml"
xmlns:xf="http://www.w3.org/2002/xforms"
xmlns:xxf="http://orbeon.org/oxf/xml/xforms"
xmlns:ev="http://www.w3.org/2001/xml-events"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xh:head>
<xf:model>
<xf:instance id="main-instance">
<data>
<value>true</value>
</data>
</xf:instance>
<xf:instance id="items-instance">
<Items>
<Item>
<value>1</value>
<status>Show</status>
</Item>
<Item>
<value>2</value>
<status>Show</status>
</Item>
<Item>
<value>3</value>
<status>Hide</status>
</Item>
</Items>
</xf:instance>
<xf:bind id="items-bind" nodeset="instance('items-instance')/Item"/>
</xf:model>
</xh:head>
<xh:body>
<xf:select ref="instance('main-instance')/value" appearance="full">
<xf:item>
<xf:label>Include all</xf:label>
<xf:value>true</xf:value>
</xf:item>
</xf:select>
<xf:repeat ref="xxf:bind('items-bind')[instance('main-instance')/value = 'true' or status = 'Show']">
<xh:div>
<xf:output value="concat('Value: ', value, ', status: ', status)"/>
</xh:div>
</xf:repeat>
</xh:body>
</xh:html>