XForms/Repeat filter
Motivation
editYou want to dynamically display a list of items that match an input field as you type. This is character-by-character incremental filtering of possible options. The difference between this and other "suggest" options is that the list of possible options can be stored in an instance.
Method
editWe will put all of the possible choices inside of a xf:repeat statement. When the user types in the input field we will narrow down the choices using an XPath expression in the repeat xf:nodeset attribute. The XPath expression will only match items that begin with specific characters in the xf:input field.
Screen Images
editBefore Filter
editAfter Filter
editLink to working XForms application
editSample Program
edit<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:xs="http://www.w3.org/2001/XMLSchema">
<head>
<xf:model>
<xf:instance id="data" xmlns="">
<data>
<item>
<title>red</title>
</item>
<item>
<title>orange</title>
</item>
<item>
<title>yellow</title>
</item>
<item>
<title>green</title>
</item>
<item>
<title>blue</title>
</item>
<item>
<title>indigo</title>
</item>
<item>
<title>violet</title>
</item>
<item>
<title>black</title>
</item>
<item>
<title>biege</title>
</item>
<item>
<title>brown</title>
</item>
</data>
</xf:instance>
<xf:instance id="search" xmlns="">
<search>
<filter />
</search>
</xf:instance>
<xf:instance id="selected-data" xmlns="">
<data>
<item />
</data>
</xf:instance>
</xf:model>
</head>
<body>
<xf:input ref="instance('search')/filter" incremental="true">
<xf:label>Starts with filter: </xf:label>
</xf:input>
<br />
<xf:repeat
nodeset="instance('data')/item[title[starts-with(., instance('search')/filter)]]"
id="data-list">
<xf:trigger>
<xf:label>Select</xf:label>
<xf:action ev:event="DOMActivate">
<xf:setvalue
ref="instance('selected-data')/item"
value="instance('data')/item[title[starts-with(.,instance('search')/filter)]][index('data-list')]/title" />
</xf:action>
</xf:trigger>
<xf:output ref="title" />
</xf:repeat>
<xf:output ref="instance('selected-data')/item">
<xf:label>Selected: </xf:label>
</xf:output>
</body>
</html>
Discussion
editThe most challenging part of this example is the value attribute of the setvalue element:
<xf:setvalue
ref="instance('selected-data')/item"
value="instance('data')/item[title[starts-with(.,instance('search')/filter)]][index('data-list')]/title" />
We are setting the item of the selected-data instance:
ref=instance('selected-data')/item
We need to set it to the selected value - but the 2nd part - [index('data-list')]
- is the numeric index of the selected item in the filtered list. This is why we 1st have to filter the list and 2nd choose the right index out of the filtered list.
Just like in the restriction of the nodeset attribute of the repeat we are re-running the XPath expression and getting all of the items that start with the text in the filter.
instance('data')item[title[starts-with(.,instance('search')/filter)]]
Note that this is exactly the same expression as in the repeat nodeset attribute.
This list is additionally constrained with the position of the selected item on the data-list:
[index('data-list')]
We are really doing consecutive filters on two sets of data. The first generates the same list as the search. The second uses the item selected to select the correct item.
Attribution
editThis example was originally posted on the mozilla XForms developer group by Chris Sw... on Aug 1st of 2007. A bug was found and fixed by Sivaram Arabandi.