XForms tutorial - Chapter 5 - Getting serious with validation

Previous chapter | Next chapter

Of course, we want to validate the input that the customer provides in our forms. Ultimately, we do this on submission but ideally, we like to perform validation as soon as the customer enters his information. This will help the customer to not waste any time. Validations come in two kinds. First of all, we have to make sure that something was entered. We did this using the required binding rule earlier. Secondly, we may want to verify if the format the entered data meets some kind of standard.

Once we have defined a control as required, the next step is to verify the input provided by the user meets some specific conditions. We can add type information to a control by specifying the type of the node in the model. You may already have noticed this line in the model instance.

Email validation

The email datatype is a standard datatype in the XForms namespace. Other standard XForms datatypes (i.e., also included in the XForms namespace) are the primitive schema datatypes and derived types. XForms then adds some specific datatypes, such as: 

  • listItem
  • listItems
  • dateTimeDuration
  • yearMonthDuration
  • email
  • card-number (not full credit card number validation, however).

The particular xsi:type="xf:email" addition will validate the entered email address against the typed email address. Since we have added the incremental attribute to this particular input and set its value to true, the information entered is checked as it is being typed in (in other words: there is continuous communication between client and server to validate the input), assuming scripting is enabled. The incremental attribute is a very powerful feature, that should be used only when it makes sense.

In this case, it does something unpleasant: while typing it tells the customer the email address is incorrect, until it meets the complete standard. All the way while typing john.mayr@acme.com the alert message will be displayed until the customer has started typing the extension of the domain name.

For specific controls, such as typed controls, it is recommended to set the incremental attribute to false.

Other validations

Generally speaking, we may define specific validations using common schema technology. We can attach a schema to an instance in a very simple way by adding the schema=”…” attribute to the <xf:model> line.

Another way to add validations is to use the powerful matches function. This allows us to use regular expressions in our XPath expressions.

Previous chapter | Next chapter

Email

<email xsi:type="xf:email" />

Email required

<email  required="true" xsi:type="xf:email" incremental="false" />

Building number

<number  required="true" xsi:type="xf:integer" />

The code so far

<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>Events Application Form</title>
    <xf:model>
      <xf:instance xmlns="">
        <applicationdata>
          <!-- information re individual applicant -->
          <citizen_company required="true" />
		  <citizen>
	          <firstname required="true" />
	          <middlename />
	          <lastname required="true" />
	          <street required="true" />
	          <number required="true" xsi:type="xf:integer"/>
	          <postalcode required="true" />
	          <city required="true" />
		  </citizen>
          <!-- information re company applicant -->
		  <company>
	          <name required="true" />
	          <street required="true" />
	          <number required="true" xsi:type="xf:integer"/>
	          <postalcode required="true" />
	          <city required="true" />
		  </company>
		  <contact>
	          <phone />
	          <email required="true" xsi:type="xf:email" />
		  </contact>
		  <event>
			<number_visitors />
		  	<plan xsi:type="xsd:base64Binary" filename="" mediatype=""></plan>
		  	<start>
				<date xsi:type="xf:date"/>
				<time xsi:type="xf:time"/>
			</start>
		  	<end>
				<date xsi:type="xf:date"/>
				<time xsi:type="xf:time"/>
			</end>
		  	<location>
	          <street required="true" />
	          <number required="true" xsi:type="xf:integer"/>
	          <postalcode required="true" />
	          <city required="true" />
			</location>
		  </event>
        </applicationdata>
      </xf:instance>
      <xf:instance xmlns="" id="resources">
        <resources>
          <texts>
            <!-- Step 0 -->
            <citizen_company>
              <label>Are you applying as an individual citizen or do you represent a company?</label>
              <hint>Please make a selection.</hint>
              <alert>This selection is required.</alert>
              <help>Indicate if you apply as a citizen or on behalf of a company.</help>
            </citizen_company>
			<citizen>
	            <firstname>
	              <label>Your first name</label>
	              <hint>Please enter your first name.</hint>
	              <alert>This information is required.</alert>
	              <help>Your first name could be 'John'</help>
	            </firstname>
	            <middlename>
	              <label>Your middle name</label>
	              <hint>Please enter your middle name.</hint>
	              <alert></alert>
	              <help>Not everybody has a middle name.</help>
	            </middlename>
	            <lastname>
	              <label>You last name</label>
	              <hint>Please enter your last name.</hint>
	              <alert>This information is required.</alert>
	              <help>Your last name could be 'Smith'</help>
	            </lastname>
	            <street>
	              <label>Your street name</label>
	              <hint>Please enter the street name.</hint>
	              <alert>This information is required.</alert>
	              <help>The street name could be 'Broad Street'</help>
	            </street>
	            <number>
	              <label>Your building number</label>
	              <hint>Please enter your building number.</hint>
	              <alert>This information is required.</alert>
	              <help>The building number could be '27'</help>
	            </number>
	            <postalcode>
	              <label>Postal code</label>
	              <hint>Please enter the postal code.</hint>
	              <alert>This information is required.</alert>
	              <help>The postal code could be '1234 AB'</help>
	            </postalcode>
	            <city>
	              <label>Your city name</label>
	              <hint>Please enter your city name.</hint>
	              <alert>This information is required.</alert>
	              <help>The city name could be 'London'</help>
	            </city>
			</citizen>
            <company>
				<name>
	              <label>Your company name</label>
	              <hint>Please enter your company's name.</hint>
	              <alert>This information is required.</alert>
	              <help>Your company's name could be 'Acme'</help>
  			    </name>
				<street>
	              <label>Your street</label>
	              <hint>Please enter your street.</hint>
	              <alert>This information is required.</alert>
	              <help>Your street could be 'Main Street'</help>
  			    </street>
				<number>
	              <label>Your building number</label>
	              <hint>Please enter your building number.</hint>
	              <alert>This information is required.</alert>
	              <help>Your building number could be '26'</help>
  			    </number>
				<postalcode>
	              <label>Your postal code</label>
	              <hint>Please enter your postal code.</hint>
	              <alert>This information is required.</alert>
	              <help>Your postal code could be '1234 AB'</help>
  			    </postalcode>
				<city>
	              <label>Your city name</label>
	              <hint>Please enter your city's name.</hint>
	              <alert>This information is required.</alert>
	              <help>Your city's name could be 'Amsterdam'</help>
  			    </city>
            </company>
			<contact>
	            <phone>
	              <label>Your phone number</label>
	              <hint>Please enter your phone number.</hint>
	              <alert></alert>
	              <help>The phone number could be '+31152513700'</help>
	            </phone>
	            <email>
	              <label>Your email address</label>
	              <hint>Please enter your email address.</hint>
	              <alert>This information is required.</alert>
	              <help>You email address could be 'john@mydomain.com'</help>
	            </email>			
			</contact>
          </texts>
          <options>
            <citizen_company>
              <option value="">Please select...</option>
              <option value="citizen">I am a citizen</option>
              <option value="company">I represent a company</option>
            </citizen_company>
          </options>
        </resources>
      </xf:instance>
	  <xf:bind nodeset="//*[@required='true']" required="true()" />
	  <xf:bind nodeset="//name" relevant="instance()/citizen_company='company'"/>
	  <xf:bind nodeset="instance()/citizen" relevant="instance()/citizen_company='citizen'"/>	
	  <xf:bind nodeset="instance()/company" relevant="instance()/citizen_company='company'"/>	  
	  <xf:bind nodeset="instance()/contact" relevant="instance()/citizen_company!=''"/>	  
  	  <xf:submission id="send" method="post" action="http://www.proforms.biz/post/submit/be4f7106-1715-491e-8dea-bac55eeec9e4" 
         ref="instance()" serialization="application/xml" 
          validate="true" replace="none">
		<xf:message ev:event="xforms-submit-error" if="event('error-type')='validation-error'">
           Validation failed.
         </xf:message>
		</xf:submission>
    </xf:model>
    <resources></resources>
  </head>
  <body>
    <!-- reference the default instance -->
    <xf:group ref="." appearance="full">
      <xf:label>Events Application Form</xf:label>
      <!-- reference the value in the instance -->
      <xf:select1 ref="citizen_company" appearance="compact" incremental="true">
        <xf:label ref="instance('resources')/texts/citizen_company/label"></xf:label>
        <xf:alert ref="instance('resources')/texts/citizen_company/alert"></xf:alert>
        <xf:help ref="instance('resources')/texts/citizen_company/help"></xf:help>
        <xf:hint ref="instance('resources')/texts/citizen_company/hint"></xf:hint>
        <xf:itemset nodeset="instance('resources')/options/citizen_company/*">
          <xf:label ref="." />
          <xf:value ref="@value" />
        </xf:itemset>
      </xf:select1>
    </xf:group>
    <xf:group ref=".">
		<xf:label>Citizen</xf:label>
      <xf:input ref="citizen/firstname">
        <xf:label ref="instance('resources')/texts/citizen/firstname/label" />
        <xf:hint ref="instance('resources')/texts/citizen/firstname/hint" />
        <xf:alert ref="instance('resources')/texts/citizen/firstname/alert" />
        <xf:help ref="instance('resources')/texts/citizen/firstname/help" />
      </xf:input>
      <xf:input ref="citizen/middlename">
        <xf:label ref="instance('resources')/texts/citizen/middlename/label" />
        <xf:hint ref="instance('resources')/texts/citizen/middlename/hint" />
        <xf:alert ref="instance('resources')/texts/citizen/middlename/alert" />
        <xf:help ref="instance('resources')/texts/citizen/middlename/help" />
      </xf:input>
      <xf:input ref="citizen/lastname">
        <xf:label ref="instance('resources')/texts/citizen/lastname/label" />
        <xf:hint ref="instance('resources')/texts/citizen/lastname/hint" />
        <xf:alert ref="instance('resources')/texts/citizen/lastname/alert" />
        <xf:help ref="instance('resources')/texts/citizen/lastname/help" />
      </xf:input>
      <xf:input ref="citizen/street">
        <xf:label ref="instance('resources')/texts/citizen/street/label" />
        <xf:hint ref="instance('resources')/texts/citizen/street/hint" />
        <xf:alert ref="instance('resources')/texts/citizen/street/alert" />
        <xf:help ref="instance('resources')/texts/citizen/street/help" />
      </xf:input>
      <xf:input ref="citizen/number" incremental="true">
        <xf:label ref="instance('resources')/texts/citizen/number/label" />
        <xf:hint ref="instance('resources')/texts/citizen/number/hint" />
        <xf:alert ref="instance('resources')/texts/citizen/number/alert" />
        <xf:help ref="instance('resources')/texts/citizen/number/help" />
      </xf:input>
      <xf:input ref="citizen/postalcode" incremental="true">
        <xf:label ref="instance('resources')/texts/citizen/postalcode/label" />
        <xf:hint ref="instance('resources')/texts/citizen/postalcode/hint" />
        <xf:alert ref="instance('resources')/texts/citizen/postalcode/alert" />
        <xf:help ref="instance('resources')/texts/citizen/postalcode/help" />
      </xf:input>
      <xf:input ref="citizen/city">
        <xf:label ref="instance('resources')/texts/citizen/city/label" />
        <xf:hint ref="instance('resources')/texts/citizen/city/hint" />
        <xf:alert ref="instance('resources')/texts/citizen/city/alert" />
        <xf:help ref="instance('resources')/texts/citizen/city/help" />
      </xf:input>
    </xf:group>
    <xf:group>
		<xf:label>Company</xf:label>
      <xf:input ref="company/name" incremental="true">
        <xf:label ref="instance('resources')/texts/company/name/label" />
        <xf:hint ref="instance('resources')/texts/company/name/hint" />
        <xf:alert ref="instance('resources')/texts/company/name/alert" />
        <xf:help ref="instance('resources')/texts/company/name/help" />
      </xf:input>
      <xf:input ref="company/street" incremental="true">
        <xf:label ref="instance('resources')/texts/company/street/label" />
        <xf:hint ref="instance('resources')/texts/company/street/hint" />
        <xf:alert ref="instance('resources')/texts/company/street/alert" />
        <xf:help ref="instance('resources')/texts/company/street/help" />
      </xf:input>
      <xf:input ref="company/number" incremental="true">
        <xf:label ref="instance('resources')/texts/company/number/label" />
        <xf:hint ref="instance('resources')/texts/company/number/hint" />
        <xf:alert ref="instance('resources')/texts/company/number/alert" />
        <xf:help ref="instance('resources')/texts/company/number/help" />
      </xf:input>
      <xf:input ref="company/postalcode" incremental="true">
        <xf:label ref="instance('resources')/texts/company/city/label" />
        <xf:hint ref="instance('resources')/texts/company/city/hint" />
        <xf:alert ref="instance('resources')/texts/company/city/alert" />
        <xf:help ref="instance('resources')/texts/company/city/help" />
      </xf:input>
    </xf:group>
	<xf:group>
		<xf:label>Contact information</xf:label>
      <xf:input ref="contact/phone" incremental="true">
        <xf:label ref="instance('resources')/texts/contact/phone/label" />
        <xf:hint ref="instance('resources')/texts/contact/phone/hint" />
        <xf:alert ref="instance('resources')/texts/contact/phone/alert" />
        <xf:help ref="instance('resources')/texts/contact/phone/help" />
      </xf:input>
      <xf:input ref="contact/email" incremental="false">
        <xf:label ref="instance('resources')/texts/contact/email/label" />
        <xf:hint ref="instance('resources')/texts/contact/email/hint" />
        <xf:alert ref="instance('resources')/texts/contact/email/alert" />
        <xf:help ref="instance('resources')/texts/contact/email/help" />
      </xf:input>
	</xf:group>
	<xf:group>
			<xf:label>Details about the event</xf:label>
			<xf:textarea ref="description">
                <xf:label>Give a short description of the event</xf:label>
                <xf:hint>Enter the text of your message here</xf:hint>
            </xf:textarea>
			<xf:upload ref="plan" incremental="true" mediatype="image/*">
                <xf:label>Plan of the location</xf:label>
                <xf:filename ref="@filename"/>
                <xf:mediatype ref="@mediatype"/>
            </xf:upload>
			<xf:range  ref="number_visitors" start="10" end="1000" step="10" appearance="full" incremental="true"> 
            	<xf:label>Number of visitors</xf:label>
            	<xf:hint>choose a range between : 10 and 1000</xf:hint>
            	<xf:help>choose a range between : 10 and 1000</xf:help>
	        </xf:range>	
	</xf:group>
	<xf:trigger><xf:label>Update</xf:label></xf:trigger>	
	<xf:submit>
		<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>