XForms/Dynamic Labels

Motivation

edit

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.

Screen Image

edit
 
Examples of XForms Dynamic Labels
edit

XForms Example of Dynamic Labels

Sample Program

edit
<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

edit

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>

Credits

edit

This example was originally inspired by Kurt Cagle.

Next Page: Suggesting Items | Previous Page: Select and Group
Home: XForms