XRX/Server Field Validation

< XRX

Motivation edit

You have data in a form field that you want to validate on the server. For example, you want to validate that a zip code or postal code is valid. You want to perform the validation on the server, because loading the full data set into the web browser for validation would slow down the form loading time.

Method edit

Sending data to a remote server and getting the results back without reloading an entire form is a central feature of XForms. The beauty of XRX is that this can be done without writing a single line of JavaScript. We will demonstrate the technique using several components.

  1. We will create a server-side RESTful component that will return a simple "OK" message if the field passes the server side checks or an error message if it fails.
  2. We will add an action to the field that will detect if the value in that field changes. This can be done for each character the user types or when the user tabs out of the field.
  3. This action will trigger a submission to the server and return the result into the model. Both the input parameters to the RESTful service and the results of the service will be stored in separate instance in the model.

Sample Code edit

In this example we will check to see if data entered is a valid ZIP Code (a US postal code).

Here is the sample code from the form:

Input Field edit

In the following code we add an XForms action to the input. The event we trigger on is the DOMFocusOut which is triggered when the users tab out of the field. We perform two actions.

  1. We put the value in the outgoing instance using the xf:setvalue function.
  2. We trigger the RESTful web service.

Sample Code Fragement for Input Field and Action edit

<xf:input ref="zip-code" incremental="true">
    <xf:label>Zip Code:</xf:label>
    <xf:hint>Five digit zip code</xf:hint>
    <xf:action ev:event="DOMFocusOut">
        <!-- copy the value in the form to the outgoing submission instance -->
        <xf:setvalue ref="instance('zip-check')/zip-code" value="instance('save-data')/zip-code"/>
        <xf:send submission="zip-check-submission"/>
    </xf:action>
</xf:input>

Submission Element edit

Here is the additional submission element that is added to the XForms model. The method is HTTP GET which adds all of the parameters in our outgoing "zip-check" instance to the end of the URL. The action is the name of the XQuery script. In this case I use a relative path that is common if the XQuery check is in the same collection as the XQuery script that generates the XForms application.

<xf:submission id="zip-check-submission" method="get" action="zip-check.xq" 
       ref="instance('zip-check-submission')" 
       replace="instance" instance="zip-check-results" 
       separator=";"/>

Instances for Outgoing Value and Incoming Results edit

 <!-- store the outgoing query parameters for the zip check. -->
    <xf:instance id="zip-check">
        <data xmlns="">
            <zip-code/>
        </data>
    </xf:instance>
    
    <!-- a place to store the results of a check -->
    <xf:instance id="zip-check-results">
        <data xmlns=""/>
    </xf:instance>

This will cause the results of the Query to be URL paramters.

zip-check.xq?zip-code=55426

Sample Server-side XQuery edit

Sample Server-Side XQuery edit

xquery version "1.0";

let $file-path := '/db/org/syntactica/wiki-books/xrx/field-server-check/zip-codes.xml'
let $zip-codes := doc($file-path)/code-table/items/item/value

let $zip-code := request:get-parameter('zip-code', '')

return
<results>{
   if (some $item in $zip-codes satisfies ($zip-code = $item))
     then
        <message class="ok">OK</message>
     else
        <message class="error">{concat('zip-code: ', $zip-code, ' is not valid.')}</message>
}</results>

Sample Response for Correct Value edit

<results>
   <message class="ok">OK</message>
</results>
<results>
   <message class="error">zip-code: 55999 is not valid.</message>
</results>

Sample Data File edit

<code-table>
    <name>zip-code</name>
    <items>
        <item>
            <label>St. Louis Park, MN</label>
            <value>55426</value>
        </item>
        <item>
            <label>Mendota Heights, MN</label>
            <value>55118</value>
        </item>
        <item>
            <label>Minneapolis, MN</label>
            <value>55401</value>
        </item>
        <item>
            <label>Edina, MN</label>
            <value>55439</value>
        </item>
    </items>
</code-table>

Full XForms Example edit

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ev="http://www.w3.org/2001/xml-events"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xf="http://www.w3.org/2002/xforms">
    <head>
        <title>Form Title</title>
        <style type="text/css"><![CDATA[
            
            @namespace xf url("http://www.w3.org/2002/xforms");
            body {
                font-family:Helvetica, sans-serif;
            }
            ]]>
            </style>
        <xf:model>

            <xf:instance id="save-data">
                <data xmlns="">
                    <zip-code>44526</zip-code>
                </data>
            </xf:instance>
            
            <xf:instance id="zip-check">
                <data xmlns="">
                    <zip-code>44526</zip-code>
                </data>
            </xf:instance>
            
            <!-- place to store the results of a the zip code -->
            <xf:instance id="zip-check-results">
                <data xmlns="">
                    <id/>
                </data>
            </xf:instance>

            
            
            <xf:submission id="zip-check-submission" method="get" action="zip-check.xq"
                ref="instance('zip-check')" replace="instance" instance="zip-check-results"
                separator=";"/>
            
        </xf:model>
    </head>
    <body>

        <xf:input ref="instance('save-data')/zip-code" incremental="true">
            <xf:label>Zip Code:</xf:label>
            <xf:hint>Five digit zip code.</xf:hint>
            <xf:action ev:event="DOMFocusOut">
                <!-- copy the value in the form to the outgoing submission instance -->
                <xf:setvalue ref="instance('zip-check')/zip-code"
                    value="instance('save-data')/zip-code"/>
                <xf:send submission="zip-check-submission"/>
            </xf:action>
        </xf:input>

        <xf:output ref="instance('zip-check-results')/message">
            <xf:label>Response:</xf:label>
        </xf:output>

        <xf:trigger submission="zip-check-submission">
            <xf:label>Check Zip Code</xf:label>
        </xf:trigger>
    </body>
</html>

Discussion edit

This technique can be employed even if the data sets are small or the validation checks simple. The form designer must consider the trade-off between storing a validation rule in the form and the overhead of going to and from a service. There are several variations of this pattern. One variation is using the "suggest" pattern to suggest one of several values as the user enters data. In this cast each character that changes may trigger a new list of possible values.