------ Input Validation ------ Jesse Kuhnert ------ 22 July 2006 ------ Input Validation The tapestry validation system provides a very powerful means of validating data intuitively on most of the form element components, such as {{{../components/form/textfield.html}TextField}}, {{{../components/form/textarea.html}TextArea}}, {{{../components/form/checkbox.html}Checkbox}}, and so forth. All of these components implement the interface {{{../apidocs/org/apache/tapestry/form/IFormComponent.html}IFormComponent}} , and include the necessary hooks to fit into the overall validation framework. Localization, server-side, and client side validation are handled by the framework, as well as the ability to extend or override most of the built in functionality to suit your purposes as you see fit. Validation has evolved over time (the first attempt at proper validation using Tapestry occured back in 2001). Through Tapestry 3, validation was limited to the <> component (which is now gone). For Tapestry 4, the APIs related to validation were effectively rewritten, resulting in a more powerful, more extensible approach that can be used with all kinds of form element components. * FieldLabel component Generally speaking, every form input component ({{{../components/form/textfield.html}TextField}}, etc.) will be paired with a {{{../components/form/fieldlabel.html}FieldLabel}} component. The FieldLabel is responsible for generating the HTML <<<\>>> element, which is extremely effective for accessible user interfaces (user interfaces that work for people with visual disabilities). Typical usage: +------------------------------------------ +------------------------------------------ At runtime, this may render as: +------------------------------------------ +------------------------------------------ However, this is not all there is to FieldLabel. An important part of validation is <<>> of fields, to mark when they contain errors. This is one of the responsibilities of {{{../apidocs/org/apache/tapestry/valid/IValidationDelegate.html}IValidationDelegate}} ... decorating fields and labels. If the above form is submitted without specifying a user name, the userName field will be in error. The page will be redisplayed to show the user the error message and the decorated fields and labels. The <<>> decoration is primitive, but effective: +---------------------------------------------- ** +---------------------------------------------- By subclassing the default implementation of {{{../apidocs/org/apache/tapestry/valid/IValidationDelegate.html}IValidationDelegate}} (the {{{../apidocs/org/apache/tapestry/valid/ValidationDelegate.html}ValidationDelegate}} class), you can change how these decorations are rendered. It then becomes a matter of providing this custom validation delegate to the {{{../components/form/form.html}Form}} component, via its delegate parameter. This is covered in more detail shortly. * Field validation Validation for form element components, such as {{{../components/form/textfield.html}TextField}} , is controlled by three common component parameters provided by all such components: <<>> / <<>> / and <<>>. The validators parameter provides a list of validator objects, objects that implement the {{{../apidocs/org/apache/tapestry/form/validator/Validator.html}Validator}} interface. Why a list? Unlike Tapestry 3 validation, each individual validator checks just a single <<>>. Contraints are things like minimum string length, maximum string length, minimum numeric value, etc. This is a very fine grained approach, and one that is easily extensible to new contraints. The <<>> parameter configures how the resulting input value should be translated from its generic String input form to the targeted type, like a <<>> or double. All translators implement the {{{../apidocs/org/apache/tapestry/form/translator/Translator.html}Translator}} interface. The displayName parameter is used to provide the label for the component (perhaps some day, this parameter will be renamed to "label"; why it has such a cumbersome name has been forgotten). In any case, this label is used by the matching {{{../components/form/fieldlabel.html}FieldLabel}} component, and is also incorporated into an error messages created for the component. ** validators: binding prefix The validators: binding prefix is a powerful shorthand for constructing a list of configured {{{../apidocs/org/apache/tapestry/form/validator/Validator.html}Validator}} objects. It allows a very declarative style; for example, to state that a field is required with a minimum length of four characters, the following parameter binding could be used (in a page or component specification): +------------------------------------------- +------------------------------------------- Notice that the actual type of the data isn't specified in this instance, it is implied by which parameters you specify. A specification is a comma-seperated list of entries. Each entry is in one of the following forms: * <<>> * <<>> = <<>> * <<>> * <<>> * $<<>> [] Most validator classes are <<>>: they have a property that matches their name. For example, {{{../apidocs/org/apache/tapestry/form/validator/MinDate.html}MinDate}} (which is named "minDate" has a <<>> property. A few validators are not configurable ("required" => {{{../apidocs/org/apache/tapestry/form/validator/Required.html}Required}}, for example). Validators are expected to have a public no-args constructor. They are also expected to have a <<>> property which is set from the value in brackets. The message is either a literal string, or may be prefixed with a '%' character, to indicate a localized key, resolved using the component's message catalog. When the name is prefixed with a dollary sign, it indicates a reference to a <<>> with the given name. A full validator specification might be: +------------------------------ required,email[%email-format],minLength=20[Email addresses must be at least 20 characters long.] +------------------------------ Here is a partial list of the validator classes provided and their configurable attributes. *-----------------+-------------------------------------------+ | {{{../apidocs/org/apache/tapestry/form/validator/Validator.html}Validator}} | attributes | *-----------------+-------------------------------------------+ {{{../apidocs/org/apache/tapestry/form/validator/BaseValidator.html}BaseValidator}} | <<>> *-----------------+-------------------------------------------+ {{{../apidocs/org/apache/tapestry/form/validator/Email.html}Email}} | <<>> *-----------------+-------------------------------------------+ {{{../apidocs/org/apache/tapestry/form/validator/Max.html}Max}} | <<>>> *-----------------+-------------------------------------------+ {{{../apidocs/org/apache/tapestry/form/validator/MaxDate.html}MaxDate}} | << >>> *-----------------+-------------------------------------------+ {{{../apidocs/org/apache/tapestry/form/validator/MaxLength.html}MaxLength}} | <<< maxLength= >>> *-----------------+-------------------------------------------+ {{{../apidocs/org/apache/tapestry/form/validator/Min.html}Min}} | <<< min= >>> *-----------------+-------------------------------------------+ {{{../apidocs/org/apache/tapestry/form/validator/MinDate.html}MinDate}} | <<< minDate= >>> *-----------------+-------------------------------------------+ {{{../apidocs/org/apache/tapestry/form/validator/MinLength.html}MinLength}} | <<< minLength= >>> *-----------------+-------------------------------------------+ ** translator: binding prefix Much like the <<>> binding, the {{{../apidocs/org/apache/tapestry/form/translator/Translator.html}translator}} binding can be configured with a simple comma-seperated string list to provide rules on how your incoming data should be translated. Some of these bindings are also used on the client side validation API to ensure the input format matches your translator parameters. For example, to validate and translate a TextField bound to a date object you might do something like: +---------------------------------------------------------- +---------------------------------------------------------- Currently available translator bindings: *-----------------+-------------------------------------------+ | {{{../apidocs/org/apache/tapestry/form/translator/Translator.html}Translator}} | attributes | *-----------------+-------------------------------------------+ {{{../apidocs/org/apache/tapestry/form/translator/AbstractTranslator.html}AbstractTranslator}} | <<< trim= >>> *-----------------+-------------------------------------------+ {{{../apidocs/org/apache/tapestry/form/translator/StringTranslator.html}StringTranslator}} | <<< trim=,empty= >>> *-----------------+-------------------------------------------+ {{{../apidocs/org/apache/tapestry/form/translator/FormatTranslator.html}FormatTranslator}} | <<< trim=,pattern= >>> *-----------------+-------------------------------------------+ {{{../apidocs/org/apache/tapestry/form/translator/DateTranslator.html}DateTranslator}} | <<< trim=,pattern= >>> *-----------------+-------------------------------------------+ {{{../apidocs/org/apache/tapestry/form/translator/NumberTranslator.html}NumberTranslator}} | <<< trim=,pattern=,omitZero= >>> If true (which is the default for the property), then values that are 0 are rendered to an empty string, not "0" or "0.00". This is useful in most cases where the field is optional; it allows the field to render blank when no value is present. *-----------------+-------------------------------------------+ * Extending ValidationDelegate There are a lot of scenerios where you may wish to do something more than that provided by the default, like apply a CSS class to labels in error, or even provide the ability to render the error message directly in or around the label or field. Below is a typical subclass of ValidationDelegate that provides more application-specific decorations: +--------------------------------------------------------------- /** * Provides more intelligent validation delegate support. */ public class MyValidationDelegate extends ValidationDelegate { /** * This method is overwritten so that the error message generated during * server-side validation actually appears next to the field in question. * * Don't be confused by the method names, there is a complimenting writeSuffix * for fields, as well as a pair of writeLabelSuffix/writeLabelPrefix methods to * do the same to labels. * {@inheritDoc} */ public void writePrefix(IMarkupWriter writer, IRequestCycle cycle, IFormComponent component, IValidator validator) { IFieldTracking ft = getCurrentFieldTracking(); // There is a default error renderer for fields // which simply writes the message, which is what // we want to have happen in this case. if (ft != null && ft.getErrorRenderer() != null) ft.getErrorRenderer().render(writer, cycle); } /** * Adds a class style attribute to the label if in error * {@inheritDoc} */ public void writeLabelAttributes(IMarkupWriter writer, IRequestCycle cycle, IFormComponent component) { if (isInError(component)) { writer.attribute("class", "labelError"); } } } +---------------------------------------------------------------