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.