XForms/Dynamic Labels
Motivation
Sometimes you want to change a single label on a form without loading the form. For example, after you enter the country of an address you may want to ask for a "Province" in Canada or a "State" in the US. This can also be used to load alternative labels based on the user's language.
Sample Program
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xf="http://www.w3.org/2002/xforms"> <head> <title>Example of Dynamic Label Lookup</title> <!-- based on a version by Kurt Cagle --> <style type="text/css"> @namespace xf url("http://www.w3.org/2002/xforms"); body {font-family: Ariel, Helvetica, san-serif} /* Input Control labels are right-aligned in a 200px wide column that floats to the left. */ /* This line ensures all the separate input controls appear on their own lines */ xf|input, xf|select, xf|select1, xf|textarea {display:block; margin:5px 0;} /* Makes the labels right aligned in a 200px wide column that floats to the left of the input controls. */ xf|input > xf|label, xf|select > xf|label, xf|select1 > xf|label, xf|textarea > xf|label {text-align:right; padding-right:10px; width:200px; float:left; text-align:right;} /* Put a black border and background color around all specified XForms groups and gives them both margin and padding */ xf|group {border: solid black 1px; margin:15px 5px; padding:5px; background-color:Lavender;} .group-label {text-align:left;font-weight:bold;font-size:12pt;} </style> <xf:model> <xf:instance id="data" xmlns=""> <Data> <PersonName> <PersonGivenName>John</PersonGivenName> <PersonMiddleName>Paul</PersonMiddleName> <PersonSurName>Doe</PersonSurName> </PersonName> <Address> <LocationStreetFull>123 Main St.</LocationStreetFull> <LocationCityName>Anytown</LocationCityName> <LocationStateName>MN</LocationStateName> <LocationPostalID>55123</LocationPostalID> </Address> </Data> </xf:instance> <xf:instance id="selected-country" xmlns=""> <data> <country-id>usa</country-id> </data> </xf:instance> <xf:bind id="country" nodeset="instance('selected-country')/country-id" /> <xf:instance id="label-lookup" xmlns=""> <label-lookup> <country> <country-id>usa</country-id> <data-element name="record" label="Record" /> <data-element name="PersonName" label="Person Name" /> <data-element name="PersonGivenName" label="First (Given) Name" /> <data-element name="PersonSurName" label="Last (Family) Name" /> <data-element name="PersonMiddleName" label="Middle Name" /> <data-element name="Address" label="Address" /> <data-element name="LocationStreetFull" label="Street" /> <data-element name="LocationCityName" label="City" /> <data-element name="LocationStateName" label="State" /> <data-element name="LocationCountryName" label="Country" /> <data-element name="LocationPostalID" label="Zip" /> </country> <country> <country-id>canada</country-id> <data-element name="record" label="Record" /> <data-element name="PersonName" label="Person Name" /> <data-element name="PersonGivenName" label="First Name" /> <data-element name="PersonSurName" label="Last Name" /> <data-element name="PersonMiddleName" label="Middle Name" /> <data-element name="Address" label="Address" /> <data-element name="LocationStreetFull" label="Street" /> <data-element name="LocationCityName" label="City" /> <data-element name="LocationStateName" label="Province" /> <data-element name="LocationCountryName" label="Country" /> <data-element name="LocationPostalID" label="Postal Code" /> </country> <country> <country-id>uk</country-id> <data-element name="record" label="Record" /> <data-element name="PersonName" label="Person Name" /> <data-element name="PersonGivenName" label="First Name" /> <data-element name="PersonSurName" label="Sur Name" /> <data-element name="PersonMiddleName" label="Middle Name" /> <data-element name="Address" label="Address" /> <data-element name="LocationStreetFull" label="Street" /> <data-element name="LocationCityName" label="City" /> <data-element name="LocationStateName" label="County" /> <data-element name="LocationCountryName" label="Country" /> <data-element name="LocationPostalID" label="Postal Code" /> </country> </label-lookup> </xf:instance> </xf:model> </head> <body> <p>Demonstration of Dynamic Labels</p> <xf:select1 bind="country" incremental="true"> <xf:label>Country: </xf:label> <xf:item> <xf:label>USA</xf:label> <xf:value>usa</xf:value> </xf:item> <xf:item> <xf:label>Canada</xf:label> <xf:value>canada</xf:value> </xf:item> <xf:item> <xf:label>UK</xf:label> <xf:value>uk</xf:value> </xf:item> </xf:select1> <xf:repeat nodeset="*"> <xf:group ref="."> <!-- get the label for the group of elements --> <xf:label class="group-label"> <xf:output value="concat(instance('label-lookup')/country[country-id=instance('selected-country')/country-id]/data-element[string(@name)=string(name(current()))]/@label,':')" /> </xf:label> <xf:repeat nodeset="*"> <xf:input ref="."> <xf:label> <xf:output value="concat(instance('label-lookup')/country[country-id=instance('selected-country')/country-id]/data-element[string(@name)=string(name(current()))]/@label,':')" /> </xf:label> </xf:input> </xf:repeat> </xf:group> </xf:repeat> <!-- debug <xf:output bind="country"> <xf:label>Country id:</xf:label> </xf:output> --> </body> </html>
Discussion
This example shows a nice clean elegant way to iterate over elements in a instance for groups and elements. The structure is:
<xf:repeat nodeset="*"> <!-- match the first level in the instance with groups-->
<xf:group ref=".">
<xf:repeat nodeset="*"> <!-- match the second level in the instance with inputs -->
<xf:input ref=".">
</xf:repeat>
</xf:group>
</xf:repeat>
This examples shows how the output tag can be buried inside the input labels.
<xf:input ref="">
<xf:label>
<xf:output value=""/>
</xf:label>
</xf:input>
