There's been some talk on the Tapestry developer list about radically reworking Tapestry's validation support (primarily, ValidField, FieldLabel and the helper classes ValidationDelegate and all the IValidator implementations). There's a lot of good reasons to do that ... the validation support in Tapestry is pretty good, but was written largely in 2001 and 2002, with only minor tweaks (such as client side validation) since then. Especially in Picasso, there's a lot of opportunity to improve it.
Until that happens (and based on the amount of movement on this subject, a complete overhaul is not likely to make it into the 4.0 release), people are still depending on the existing validation.
One of the most annoying, if relatively minor, problems with validation is the relationship between FieldLabel and ValidField when rendering, with decorations, inside a complex form (that is, with loops). There gets to be a nasty off-by-one problem, where the FieldLabel is out of sync with the ValidField and the wrong FieldLabel instance (within the enclosing loop) get decorated.
This is because the FieldLabel renders before the ValidField (in most Western language, left-to-right style layouts). I came up with a kind of kludge about how to approach this, where we gimic things to get the ValidField to render before the FieldLabel ... but then defer emitting that output into the response stream until the correct point.
What occurred to me this morning is that, with a modest amount of effort, we could bake this directly into the components That is, the FieldLabel could:
- Obtain its field (i.e., a IFormComponent instance)
- Create a nested IMarkupWriter
- Have the field render into the nested writer
- Store the writer as an IRequestCycle attribute, using a key that includes the form component's extendedId
- Do it's own rendering
The ValidField (and all the other IFormComponent implementations) would have to:
- See if there's a pre-renderered writer IRequestCycle attribute
- If so: close the writer (which injects the buffered markup into the outer writer) and remove the attribute
- If not: render normally
It's a bit of work, since it will require updating all the form control components. It's also a chance to get, at least, decoration of all form control components working.
In addition, I believe I'll change the Form component to provide a default IValidationDelegate even when its delegate parameter is not bound ... and change the code inside the various components to expect a non-null delegate from the form.
Finally, I've been thinking that an alternate way to define validators, using a binding prefix, would be useful, something like:
<binding name="validator" value="validator:string,required,minLength=3"/>