Interface used to track validation errors in forms and {@link IFormComponent form element component}s (including {@link org.apache.tapestry.form.AbstractTextField} and its subclasses).
In addition, controls how fields that are in error are presented (they can be decorated in various ways by the delegate; the default implementation adds two red asterisks to the right of the field).
Each {@link org.apache.tapestry.form.Form} must have its own validation delegate instance.
Starting with release 1.0.8, this interface was extensively revised (in a non-backwards compatible way) to move the tracking of errors and invalid values (during a request cycle) to the delegate. It has evolved from a largely stateless conduit for error messages into a very stateful tracker of field state.
Starting with release 1.0.9, this interface was again reworked, to allow tracking of errors in {@link IFormComponent form components}, and to allow unassociated errors to be tracked. Unassociated errors are "global", they don't apply to any particular field.
Fields vs. Form Element Components
For most simple forms, these terms are pretty much synonymous. Your form will render normally, and each form element component will render only once. Some of your form components will be {@link ValidField} components and handle most of their validation internally (with the help of {@link IValidator} objects). In addition, your form listener may do additional validation and notify the validation delegate of additional errors, some of which are associated with a particular field, some of which are unassociated with any particular field.
But what happens if you use a {@link org.apache.tapestry.components.Foreach} or {@link org.apache.tapestry.form.ListEdit} inside your form? Some of your components will render multiple times. In this case you will have multiple fields. Each field will have a unique field name (the {@link org.apache.tapestry.FormSupport#getElementId(IFormComponent) element id}, which you can see this in the generated HTML). It is this field name that the delegate keys off of, which means that some fields generated by a component may have errors and some may not, it all works fine (with one exception).
The Exception
The problem is that a component doesn't know its field name until its render()
method is invoked (at which point, it allocates a unique field name from the {@link org.apache.tapestry.IForm#getElementId(org.apache.tapestry.form.IFormComponent)}. This is not a problem for the field or its {@link IValidator}, but screws things up for the {@link FieldLabel}.
Typically, the label is rendered before the corresponding form component. Form components leave their last assigned field name in their {@link IFormComponent#getName() name property}. So if the form component is in any kind of loop, the {@link FieldLabel}will key its name, {@link IFormComponent#getDisplayName() display name}and error status off of its last renderred value. So the moral of the story is don't use {@link FieldLabel}in this situation.
@author Howard Lewis Ship