The problemYou can write validation "rules" directly in XForms using the
Let's assume you want to delegate the validation of the following registration form to a service: ![]() The problems are:
The solution As it is often the case, there are multiple possible approaches, each with its benefits and drawbacks. Let's explore a solution based on the idea of annotating the original instance. The validation response The instance to which the controls are bound is: <user-info>This instance is submitted to a validation service, which returns a document that has the following form: <v:validation-result xmlns:v="http://www.example.com/validation"> <v:data> <user-info> <first-name>Tom</first-name> <last-name>Smith</last-name> <customer-number v:alert="This customer number is unknown to the system"v:valid="false">1234-4567-7890</customer-number> </user-info> </v:data> <v:global-errors> <v:global-error v:alert="The first and last name do not match an existing customer"/> </v:global-errors></v:validation-result>In this document:
Calling the serviceYou call the service by triggering the following submission:<xforms:submission id="validation-submission" ref="instance('user-info')" resource="/apps/xforms-sandbox/samples/howto/external-validation-response.xml" method="post" replace="instance" instance="validation-result"> <xforms:delete ev:event="xforms-submit" nodeset="//@v:*"/> <xforms:action ev:event="xforms-submit-done"> <xforms:insert nodeset="." origin="instance('validation-result')/v:data/*"/> <xforms:dispatch name="fr-visit-all" targetid="error-summary"/> </xforms:action></xforms:submission>This submission:
Marking fields as invalidYou tell XForms that all the nodes with <xforms:bind nodeset="//*" constraint="not(@v:valid = 'false')"/>Then for each field, you reference the message included in v:alert attribute as the alert. For instance, for first name: <xforms:input ref="first-name"> <xforms:label>First name</xforms:label> <xforms:alert ref="@v:alert"/></xforms:input>If your validation service does not return user-friendly messages, you can have the message in-line in the XForms view, and use that message instead. Global errorsSince global errors are not related to a control in particular, you need to tell the error summary component about those with thefr:errors element:<fr:error-summary id="error-summary" observer="form-group">The error summary won't provide a link to any control for global errors, as they are not attached to a field in particular. The first error in this screenshot is a global error: Handling failures of the validation service What should you do when the validation service fails? Most likely you'll want to report a global error, rather than not show any error at all. You can do so by having an instance that contains the error to display if the validation service fails, using the same format used by the validation service:<xforms:instance id="validation-result-submit-error"> <v:validation-result xmlns:v="http://www.example.com/validation"> <v:global-errors> <v:global-error v:alert="The document could not be validated as there has been a communication error; please try again or contact technical support"/> </v:global-errors> </v:validation-result></xforms:instance>Then, on the submission that runs the validation service, you add a handler for xforms-submit-error, which uses the above instance and tells the error summary to show its errors:<xforms:action ev:event="xforms-submit-error"> <xforms:insert nodeset="instance('validation-result')" <xforms:dispatch name="fr-visit-all" targetid="error-summary"/></xforms:action>Constraint This technique posses the following constraint:
Run it and get the source |

