The problem- You have a form with an
<xforms:select> or <xforms:select1> populated by data coming from an instance, e.g.:
<xforms:instance id="states">
<states>
<state value="ma">Maryland</state>
<state value="ca">California</state>
<state value="co">Colorado</state>
<state value="nv">Nevada</state>
</states>
</xforms:instance>
- A user fills out the form selecting California, and saves the data. At this point the XML contains the value
ca. - For some reason, the data in the states instance is changed and California is removed.
- A user comes back to edit the form that was saved earlier.
Because California has been removed, the value ca is not valid anymore. You want to: - Signal users that the value is not valid, so they should choose another value.
- Give users some indication regarding the value previously selected, which can help them select a new value. For instance, if California was the location of a warehouse to ship items from, they might want to now select Nevada, as it is the closest state.
The solution
For end users
First, let's see how the UI looks like for end users. If the currently selected value is in the itemset, users see a drop-down as they normally would with <xforms:select1 appearance="minimal">: If California was selected in the past, but that item is not available anymore, then the drop-down will show as follows, inviting users to select a new value: When the field is read-only, since users can't change the value, only the message shows: For form authors Use The logic of this particular sort of <xforms:select1> is encapsulated inside a component implemented in XBL. You use it in place of an <xforms:select1> as follows: <howto:changing-itemset-select1 value-ref="." label-ref="@label" valid-ref="@valid" appearance="minimal" id="initial-state">
<xforms:label>State: </xforms:label>
<xforms:itemset nodeset="instance('states')/state">
<xforms:value ref="@value"/>
<xforms:label ref="."/>
</xforms:itemset>
</howto:changing-itemset-select1>
Instead of having one ref attribute, this component has 3, so it can store in the instance: - In the node pointed to by
value-ref, the value selected by the user. This is the equivalent of the ref in <xforms:select1>. - In the node pointed to by
label-ref, the label for the selected value. This label (e.g. "California") is used in the message telling users that the selected value is not available anymore, as when that happens you can't get the label based on the value from the itemset anymore. - In the node pointed to by
valid-ref, whether the control is valid, i.e. whether the selected value is part of the itemset. This is necessary because components cannot influence the validity of the node; they can only store a value in that node. So you would then write your own <xforms:bind> using the validity value provided by the component, as in:
<xforms:bind nodeset="state" constraint="@valid = 'true'"/>
The content of the <howto:changing-itemset-select1> is identical to what you would have in an <xforms:select1>. The example above shows a label and an itemset, but you can also have an alert, hint, and help, as necessary. ImplementationThe component is implemented in XBL, and you'll find the implementation in the <xbl:xbl> element inline in the example linked below, after the <xforms:model>. It is set in a "howto" namespace. Typically, you will want to define your own namespace for your own components and put the XBL code in a separate file instead of inlining it with your form: - Say your company is Acme, you define a namespace
xmlns:acme="http://www.acme.com/xbl". - You define the component in that namespace (instead of
xmlns:howto="http://www.orbeon.com/howto"). - You store the XBL in a separate file in the resources:
/xbl/acme/changing-itemset-select1/changing-itemset-select1.xbl. - You tell Orbeon Forms that it should look for XBL components in the
http://www.acme.com/xbl namespace under the /xbl/acme directory by defining the following property:
<property as="xs:string" name="oxf.xforms.xbl.mapping.acme" value="http://www.acme.com/howto"/>
Run it and get the source
|
|