XForms tutorial - Chapter 9 - Prefilling and using information from other sources

Previous chapter | Next chapter

So far, our form provides a number of user inputs that are empty (or offer hardcoded selections, for instance the citizen / company selector). Now there may be several situations, where we might want to prefill controls with some information.The easiest way to do this is by entering this information directly into the model instance. You can just experiment a little bit:

<citizen_city required="true" >Amsterdam</citizen_city>

When the form is rendered, the city input field will already be filled with ‘Amsterdam’.

Now this is not a very flexible way to do things. Ideally, we would like to prepopulate an input (or rather a node in the instance) with a (list of) values from another system.

For now, let us assume there is a web service somewhere that offers a list of all city names. We could then use this list to prepopulate a control and let the user select a city from the list. Let us see how to do that.

First, we create an extra (empty) instance we call listofcities.

<!-- this list will be filled on the fly by a web service call -->              
<xf:instance xmlns="" id="listofcities">
<instanceData>
</instanceData>
</xf:instance>

We then add this line to the model:

<!-- Gather available Dutch cities from web service-->
<xf:submission
id="cities"
action="http://standaarden.overheid.nl/owms/terms/Gemeente.xml"
ref="instance('listofcities')"
method="post"
serialization="application/xml"
replace="instance"
instance="listofcities"
validate="true" >
</xf:submission>

This submission references the listofcities instance and replaces the complete instance with the data it receives from the URL. This particular URL is provided by a Dutch government agency but it could be any web service provided by any system or any organization.

For now we will just add a button to our form to make the call to this submission:

<xf:submit ev:event="DOMActivate" submission="cities">
<xf:label>Collect Cities</xf:label>
</xf:submit>

Our form will now display a button somewhere near the bottom of the form. When we click the button, the submission is fired and – if everything works well – the listofcities instance will be filled with data.

Using the Smartsite Workbench, we can inspect the instance data. First, make sure that the View > Instance Data pane is selected. Then, in the Instance Data pane, right-click and select the listofcities instance:

Selecting the instance

Then look at the instance. After clicking the button, you will see that the data has been replaced by downloaded information:

The instance filled with data

Obviously, this is a very powerful feature. With just a few lines of XForms code, we have filled an instance with real-life and real-time data. We can now use this data to prefill a dropdown (or, in XForms terms, a select1 control).

We can now simply use this instance to populate a select1 control:

<xf:select1 ref="city">
    <xf:label>City: </xf:label>
    <xf:hint>Enter your city.</xf:hint>
    <xf:alert>City is required.</xf:alert>
    <xf:help>Please provide the name of your city.</xf:help>
         <xf:itemset nodeset="instance('listofcities')//value/prefLabel">
               <xf:label ref="." />
               <xf:value ref="." />
         </xf:itemset>    
   </xf:select1>

This code will now take the instance listofcities data and turn it into options for the select1 control.

Select1 with citiy names

We can now replace the input for citizen/city and for company/city in our event application form with these two blocks of code:

<xf:select1 ref="citizen/citizen_city" >
<xf:label>City</xf:label>
<xf:itemset nodeset="instance('listofcities')//value/prefLabel">
<xf:label ref="." />
<xf:value ref="." />
</xf:itemset>
</xf:select1>

Now the last thing to do is to get rid of the unnecessary button and just download the cities information as the form is loaded. For this we could use the event system discussed before, but we can make life easier still by directly loading the instance from the source as the instance is being created by the XForms engine:

<!-- this list will be filled on the fly by a web service call -->                                     
<xf:instance xmlns=""
id="listofcities"
src="http://standaarden.overheid.nl/owms/terms/Gemeente.xml">
<instanceData>
</instanceData>
</xf:instance>

With this little addition, we no longer have the need for the event listener and the submit control so we will remove them again.

Now that we have 'discovered' that there is an easy way to load an external instance, we may consider offloading the resource instance to a separate XML document. This will also facilitate a separation of concerns: it will enable one person to control the logic of the form, but another, possibly unfamiliar with XForms technology, to maintain the textual content.

Below you will find a self-contained mini-form with city name prefilling. Just two powerful lines of code add a lot of functionality to this form. To summarize:

  • The declaration of in listofcities instance with the src attribute
  • The declaration of the select1 control with the itemset attribute referring to the listofcities instance.

Previous chapter | Next chapter

Complete Chapter 9 snippet

<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" 
	xmlns:xf="http://www.w3.org/2002/xforms" 
	xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns:ev="http://www.w3.org/2001/xml-events">
	<head>
		<title>New XForm</title>
		<xf:model>
			<xf:instance xmlns="">
				<formData>
					<city />
				</formData>
			</xf:instance>
			<!-- this list will be filled on the fly by a web service call -->              
			<xf:instance xmlns="" 
				id="listofcities" 
				src="http://standaarden.overheid.nl/owms/terms/Gemeente.xml">
			       <instanceData>
			       </instanceData>
			</xf:instance>			
			<xf:bind nodeset="name" required="true()" />
			
			<xf:submission id="send" 
				method="post" action="http://xforms.smartsite.nl/xformsecho" 
			ref="." serialization="multipart/form-data" validate="true" replace="none">
				<xf:message ev:event="xforms-submit-error" if="event('error-type')='validation-error'">Validation failed.</xf:message>
			</xf:submission>		
			<!-- Gather available Dutch cities from web service-->
		</xf:model>
	</head>
	<body>
		<xf:group ref="." appearance="full">
			<xf:label>Chapter 9 snippet</xf:label>
			<xf:select1 ref="city">
				<xf:label>Citye: </xf:label>
				<xf:hint>Enter your city.</xf:hint>
				<xf:alert>City is required.</xf:alert>
				<xf:help>Please provide the name of your city.</xf:help>
		       <xf:itemset nodeset="instance('listofcities')//value/prefLabel">
		             <xf:label ref="." />
		             <xf:value ref="." />
		       </xf:itemset>				
			</xf:select1>
		</xf:group>
		<noscript>
			<xf:trigger>
				<xf:label>Update screen</xf:label>
				<xf:hint>Scripting is off. Update the screen by clicking this button.</xf:hint>
			</xf:trigger>
		</noscript>
		<xf:submit class="block-ui sync">
			<xf:label>Submit</xf:label>
			<xf:message ev:observer="send" ev:event="xforms-submit-done" 
			level="ephemeral">Submit ready...</xf:message>
		</xf:submit>	
	</body>
</html>