Creating a form with validation and tooltips
- How does my form look like when validation errors are shown
- Sample Pages
- How to create validations using regular expressions
- How to create validations using a groovy script
- How to display validation error messages
- How to display the field pretty name with tooltip
- Complete presentation sheet of the document
- Client Side Validation
This sample shows how to create a form with validation and tooltips. It demonstrates regular expression validation as well as a more complex validation using a groovy script.
Download the
.How does my form look like when validation errors are shown
Sample Pages
The following pages are used:
- ValidationSample.WebHome Home page where you can find this documentation
- ValidationSample.ValidationSampleClass Class with definitions of fields, regular expressions and error message translations strings
- ValidationSample.ValidationSampleSheet Sheet presenting the document in create, edit and view mode including validation error messages
- ValidationSample.ValidationSampleTemplate Template of a document
- ValidationSample.ValidationGroovy Groovy validation script for complex validations
- ValidationSample.Translations Translations of the texts, tooltips and error messages. This shows an example of the naming conventions for tooltips and pretty names
- ValidationSample.Val, ValidationSample.Val_0 and ValidationSample.Val_1 Sample documents
How to create validations using regular expressions
To create validation first you need to define your class and set the regular expression to validate the fields you want to validate.
Then, to perform validation after a standard "Save" in the form, following code is needed:
Pay attention to the Validation Regular Expression and Validation Message fields. The first one is a Java Regular Expression pattern and the second one is a translation string. For the sample class we created we have:
- first_name
/^.{2,20}$/ -> this field needs to be between 2 characters and 20 characters. If the field can have new lines, enable the dotall mode by adding an s at the end of the regex (/^.{2,20}$/s), otherwise a regex that contains a new line will not pass validation.
val_firstname_toolong -> XWiki will lookup this translation string in the translations pages - last_name
/^.{2,20}$/ -> this field needs to be between 2 and 20 characters. - email
/.*@.*.com$/ -> this field must contain @ and finish with .com - age
no validation set for age. This will be handled by the groovy script - usphone
/^[0-9][0-9][0-9]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]$/ -> the phone number must be made of digits separated by - in the form 000-000-0000
Other Validation Regular Expression examples:
- do not match XWiki.XWikiGuest, but allow it to be inside the string: /^(?!XWiki.XWikiGuest$).*/
- forbid XWiki.XWikiGuest anywhere in the string: /^(?!.*XWiki.XWikiGuest).*/
To trigger the validation dynamically, the following Velocity code should be called:
$doc.validate()
How to create validations using a groovy script
To create complex validations you have to use a Groovy script.
Invoking Groovy script
Groovy validation script can be invoked using two approaches:
- Using HTML code in the form, the validation on a standard "Save" of a document:## Force server side validation.
<input type="hidden" name="xvalidate" value="1" />
## Specify the page that holds the Groovy script that should be used for validation.
<input type="hidden" name="xvalidation" value="ValidationSample.ValidationGroovy" /> - Or dynamically using Velocity code:## set the page, which Groovy script will be used for validation
$doc.setValidationScript("ValidationSample.ValidationGroovy")
## invoke document validation.
$doc.validate()
Groovy script sample
Here is the sample groovy script:
import com.xpn.xwiki.*;
import com.xpn.xwiki.doc.*;
import com.xpn.xwiki.objects.*;
public class Val implements XWikiValidationInterface {
public boolean validateDocument(XWikiDocument doc, XWikiContext context) {
// You can log in the app server output to check your code
// System.out.println("validation is called");
def res = true;
def obj = doc.getObject("ValidationSample.ValidationSampleClass");
def first_name = obj.getStringValue("first_name");
def last_name = obj.getStringValue("last_name");
def age = obj.getIntValue("age");
// You can log in the app server output to check your code
// System.out.println("Age: " + age);
// System.out.println("First name: " + first_name);
// System.out.println("Last name: " + last_name);
if (first_name.equals(last_name)) {
// You can log in the app server output to check your code
// System.out.println("firstname");
// This stores the validation error message. The translation string is "val_firstname_lastname"
XWikiValidationStatus.addErrorToContext("ValidationSample.ValidationSampleClass", "", "", "val_firstname_lastname", context);
res = false;
}
if (age<20 || age>24) {
// You can log in the app server output to check your code
// System.out.println("age");
// This stores the validation error message. The translation string is "val_age_incorrect"
XWikiValidationStatus.addErrorToContext("ValidationSample.ValidationSampleClass", "age", "Age", "val_age_incorrect", context);
res = false;
}
return res;
}
public boolean validateObject(BaseObject object, XWikiContext context) {
return true;
}
}
How to display validation error messages
The sheet can access the validation error messages using:
<p class="text-danger">$services.localization.render($error)</p>
#end
#foreach ($exception in $xcontext.validationStatus.exceptions)
<p class="text-danger">$exception</p>
#end
Display the validation error messages next to the field
For a given field (e.g. first name) you can show the validation error message using:
#set ($xclass = $xwiki.getDocument('ValidationSample.ValidationSampleClass').xWikiClass)
#set ($fieldDefinition = $xclass.get($fieldName))
#set ($validationMessage = $fieldDefinition.getValue('validationMessage'))
#set ($hasError = $xcontext.validationStatus.errors.contains($validationMessage))
#if ($hasError)
<p class="text-danger">$services.localization.render($validationMessage)</p>
#end
How to display the field pretty name with tooltip
We can use Bootstrap to show a tooltip after the field pretty name.
#set ($localClassReference = 'ValidationSample.ValidationSampleClass')
#set ($mandatory = true)
<dt>
<label for="${localClassReference}_0_$fieldName">
$doc.displayPrettyName($fieldName)##
#if ($mandatory && $xcontext.action == 'edit')
<sup class="text-danger">*</sup>
#end
</label>
#set ($tooltipKey = "${localClassReference}_${fieldName}_tooltip")
#if ($services.localization.get($tooltipKey) && $xcontext.action == 'edit')
<a href="#tooltip" data-toggle="popover" data-trigger="focus"
data-content="$escapetool.xml($services.localization.render($tooltipKey))">
$services.icon.renderHTML('info')
</a>
#end
</dt>
In order to activate the tooltips you need to use some JavaScript code that you can put in a JavaScriptExtension object:
// Activate all popovers.
$('[data-toggle="popover"]').popover();
});
Complete presentation sheet of the document
The content of the final presentation sheet is:
#set ($xclass = $xwiki.getDocument('ValidationSample.ValidationSampleClass').xWikiClass)
#set ($validationMessages = $collectionstool.set)
#**
* This macros displays a field and it's tool tip.
*#
#macro (showField $fieldName $mandatory)
#set ($fieldDefinition = $xclass.get($fieldName))
#set ($validationMessage = $fieldDefinition.getValue('validationMessage'))
#set ($discard = $validationMessages.add($validationMessage))
#set ($hasError = $xcontext.validationStatus.errors.contains($validationMessage))
#set ($localClassReference = $services.model.serialize($xclass.reference, 'local'))
<dt class="form-group#if ($hasError) has-error#end">
<label class="control-label" for="${localClassReference}_0_$fieldName">
$doc.displayPrettyName($fieldName)##
#if ($mandatory && $xcontext.action == 'edit')
<sup class="text-danger">*</sup>
#end
</label>
#set ($tooltipKey = "${localClassReference}_${fieldName}_tooltip")
#if ($services.localization.get($tooltipKey) && $xcontext.action == 'edit')
<a href="#tooltip" data-toggle="popover" data-trigger="focus"
data-content="$escapetool.xml($services.localization.render($tooltipKey))">
$services.icon.renderHTML('info')
</a>
#end
<span class="xHint">$fieldDefinition.getValue('hint')</span>
</dt>
<dd class="form-group#if ($hasError) has-error#end">
#set ($output = $doc.display($fieldName))
$stringtool.removeEnd($stringtool.removeStart($output, '{{html clean="false" wiki="false"}}'), '{{/html}}')
#if ($hasError)
<div class="text-danger">$escapetool.xml($services.localization.render($validationMessage))</div>
#end
</dd>
#end
#**
* This macro shows all the remaining errors (that are not bound to a particular form field).
*#
#macro (showRemainingErrors)
#set ($remainingValidationMessages = [])
#foreach ($error in $xcontext.validationStatus.errors)
#if (!$validationMessages.contains($error))
#set ($discard = $remainingValidationMessages.add($error))
#end
#end
#if ($remainingValidationMessages.size() > 0 ||
($xcontext.validationStatus.exceptions && $xcontext.validationStatus.exceptions.size() > 0))
<div class="box errormessage">
Validation errors
<ul>
#foreach ($error in $xcontext.validationStatus.errors)
<li>$escapetool.xml($services.localization.render($error))</li>
#end
#foreach ($exception in $xcontext.validationStatus.exceptions)
<li>$escapetool.xml($services.localization.render($exception))</li>
#end
</ul>
</div>
#end
#end
{{/velocity}}
{{velocity}}
#set ($discard = $xwiki.jsx.use('ValidationSample.ValidationSampleSheet'))
{{html clean="false"}}
<div class="xform">
## Force server side validation.
<input type="hidden" name="xvalidate" value="1" />
## Set the validation script.
<input type="hidden" name="xvalidation" value="ValidationSample.ValidationGroovy" />
#set ($discard = $doc.use('ValidationSample.ValidationSampleClass'))
<dl>
#showField('first_name', true)
#showField('last_name', true)
#showField('age', true)
#showField('email', true)
#showField('usphone', true)
#showField('text', false)
</dl>
#showRemainingErrors
</div>
{{/velocity}}
And don't forget about the JavaScriptExtension object on the sheet page:
// Activate all popovers.
$('[data-toggle="popover"]').popover();
// Focus the first field with validation error.
$('.form-group.has-error input, .form-group.has-error textarea, .form-group.has-error select').first().focus();
});
Client Side Validation
It's important to have server side validation for security (the client side validation can be easily bypassed) but client side validation as you type can improve the user's experience and save server load from forms submitted over and over again. To do validation on the client side we recommend using the standard HTML5 form validation as much as possible or a jQuery plugin such as jQuery Validation Plugin.