Dynamic Data Dropdown Component
Overview
This components behaves very much like an <xf:select1>
, except that the data populating the control doesn't come from an instance, but from a service. You specify the URI of the service and the data-bound select1 component takes care of calling it when necessary, retrieving the data, and using that data to populate a drop-down, list, or radio buttons.
You can also use this component to create chained select1 controls, where the value selected in the first one drives the list of values available in the second one, and so on. The following example illustrates this situation.
Example
You need 3 drop-downs: state, city, and zip code. The state drop-down lists the 50 states, the city drop-down lists all the cities in the selected state, and the zip code drop-down lists all the zip codes for the selected city. This is the situation you see in the following screenshot:
To populate the state drop-down, you use a pre-existing service that returns the list of states.This service is at the URI /xforms-sandbox/service/zip-states
. You specify that URI in the resource
attribute. Other than that, the <fr:databound-select1>
looks very much like an <xf:select1>
:
<fr:databound-select1
ref="state"
appearance="minimal"
resource="/xforms-sandbox/service/zip-states">
<xf:label>State</xf:label>
<xf:itemset ref="/states/state">
<xf:label ref="@name"/>
<xf:value ref="@abbreviation"/>
</xf:itemset>
</fr:databound-select1>
So far so good: assuming you have such a service that returns the data you need, this component saves you from having to declare a submission to call the service, to declare an instance to store the data, and to call the service when appropriate. But things become more interesting when you chain another <fr:databound-select1>
. Let's assume you have a service that returns the list cities for a state, taking that state as a parameter. As for the first drop-down, you specify the URI of that service in the resource
attribute, and use an AVT to pass the value of the parameter. The component then takes care of calling the service whenever the parameter changes, to update the data as necessary:
<fr:databound-select1
ref="city"
appearance="minimal"
resource="/xforms-sandbox/service/zip-cities?state-abbreviation={state}">
<xf:label>City</xf:label>
<xf:itemset ref="/cities/city">
<xf:label ref="@name"/>
<xf:value ref="@name"/>
</xf:itemset>
</fr:databound-select1>
Using this technique, you can chain as many drop-downs (or lists, or radio buttons) as necessary, letting the component take care of calling the service when necessary. You can see the source of an example using 3 <fr:databound-select1>
and producing the result shown in screenshot you've seen earlier.
Hiding empty dropdowns
[SINCE Orbeon Forms 4.6] The data-bound select1 component adds an attribute itemset-empty
on the element it is bound to, and populates it with either the value true
if the itemset you provide is empty, or the value false
otherwise.
You can rely on the value in the itemset-empty
attribute to hide the control when users can't select a value, if in your form it doesn't make sense to show the control when there is just no value available to select. When doing so, make sure not to make the control non-relevant in that case (e.g. don't do this an <xf:bind relevant="…">
), as when non-relevant the control is completely disabled, and it wouldn't be able to re-evaluate the AVT you provided through the resource
attribute. Instead, you should use CSS for this, for instance:
- Add an
fr:bind
or update the relevantfr:bind
for this control, adding afr:itemset-empty
custom MIP, as in:<xf:bind ref="…" fr:itemset-empty="@itemset-empty = 'true'">
. If you're doing this in Form Builder, edit the source of the form, find thexf:bind
that corresponds to your control, and add the attribute fr:itemset-empty="@itemset-empty = 'true'". Add CSS to hide the control, using the class added on the control based on the value of the custom MIP, as in:
.orbeon .fr-itemset-empty-true { display: none }
. If you're doing this in Form Builder, you can place this CSS in your custom CSS file, or if you're just using this in one form, in the source of the form, by editing the source of the form, and below thexh:title
, adding:<xh:style type="text/css"> .orbeon .fr-itemset-empty-true { display: none } </xh:style>
You can of course use the itemset-empty
attribute for other things. For instance, you can make the control required only if the itemset isn't empty, by adding, on the xf:bind,
the attribute required="@itemset-empty = 'false'"
.