Crear nuevos validadores
public class In extends AbstractValidator<String, String> { public In() { super(String.class, String.class, "in"); } public void validate(Field field, String constraintValue, MessageFormatter formatter, String value) throws ValidationException { List values = Arrays.asList(constraintValue.split("|")); if (!values.contains(value)) throw new ValidationException(buildMessage(formatter, field, constraintValue)); } public void render(Field field, String constraintValue, MessageFormatter formatter, MarkupWriter writer, FormSupport formSupport) { formSupport.addValidation(field, "in", buildMessage(formatter, field, constraintValue), constraintValue); } private String buildMessage(MessageFormatter formatter, Field field, String constraintValue) { return formatter.format(constraintValue, field.getLabel()); } }
Una vez que tenemos la clase validadora tenemos que dar a conocer a Tapestry de su existencia, para ello debemos hacer una contribución en el contenedor de dependencias:
// ...en la clase del módulo de la aplicación public static void contributeFieldValidatorSource(MappedConfiguration<String, Validato> configuration) { configuration.add("in", new In()); } ...
En este momento la podemos usar en los campos de la siguiente forma:
<t:label for="telefono">: <t:textfield t:id="telefonoField" value="producto" validate="in=coche|casa|televisión|libro" label="Producto"/>
Como cambiar el decorador por defecto
Cuando se producen errores de validación, Tapestry se encarga de decorar los campos para marcarlos como que tienen errores para de tal forma que el usuario pueda actuar en consecuencia, esto se hace sin que tengamos que hacer nada por nuestra parte. El decorador por defecto cuando hay errores marca la etiqueta y el campo en rojo y añade una imagen con una equis roja después del campo. Eso puede no ser lo que queramos ya que la imagen de la equis puede hacer que se desmaqueten las cosas si estamos justos de espacio en la pantalla.Podemos personalizar la forma en que Tapestry decora los campos por defecto añadiendo elementos antes y despues de la etiqueta del campo y del campo mismo, también podemos añadir una clase de error a la etiqueta del campo y al campo de datos. Para ello debemos extender de la clase BaseValidationDecorator que nos proporciona los métodos para realizar las personalizaciones.
En este ejemplo añadimos una clase CSS a la etiqueta y al campo cuando el valor del campo no es válido. Tendremos que modificar el estilo de esa clase CSS que añadimos («t-error») para modificar su aspecto.
... public final class AppValidationDecorator extends BaseValidationDecorator { private final Environment environment; private final MarkupWriter markupWriter; /** * @param environment * used to locate objects and services during the render * @param markupWriter */ public AppValidationDecorator(Environment environment, MarkupWriter markupWriter) { this.environment = environment; this.markupWriter = markupWriter; } @Override public void insideField(Field field) { if (inError(field)) addErrorClassToCurrentElement(); } @Override public void insideLabel(Field field, Element element) { if (field == null) return; if (inError(field)) element.addClassName(CSSClassConstants.ERROR); } private boolean inError(Field field) { ValidationTracker tracker = environment.peekRequired(ValidationTracker.class); return tracker.inError(field); } private void addErrorClassToCurrentElement() { markupWriter.getElement().addClassName(CSSClassConstants.ERROR); } }
Modificar el deccorador por defecto que usa Tapestry por defecto por el nuestro nos requerirá que modifiquemos la factoría que se encarga de devolver los decoradores.
public class AppValidationDecoratorFactory implements ValidationDecoratorFactory { private final Environment environment; public AppValidationDecoratorFactory(Environment environment) { this.environment = environment; } public ValidationDecorator newInstance(MarkupWriter writer) { return new AppValidationDecorator(environment, writer); } }
Solo nos quedaría hacer la contribución al contenedor de dependencias para usar nuestra factoría en vez la de por defecto.
// ...en la clase del módulo de la aplicación public static void bind(ServiceBinder binder) { binder.bind(ValidationDecoratorFactory.class, AppValidationDecoratorFactory.class).withId("AppValidationDecoratorFactory"); }
De esta forma podemos cambiar en todos los formularios de la aplicación la forma en que se muestran los mensajes de error de una forma muy simple. Se puede apreciar que toda la lógica de como se muestran los campos con error está en una única clase.
Macros de validaciones
Si nos encontramos en la situación en que un determinado conjunto de validaciones se repite a lo lago de los campos de la aplicación podemos definir una macro con ese conjunto de validaciones, como por ejemplo podría ser la validación de un campo contraseña.// ...en la clase del módulo de la aplicación @Contribute(ValidatorMacro.class) public static void contributeValidatorMacro(MappedConfigurationconfiguration) { configuration.add("password","required,minlength=5,maxlength=15"); }
Ahora podemos usar el validador password, que comprende que el campo sea requerido, de una longitud mínima de 5 caracteres y de 15 como máximo, como si fuera un validador más de forma declarativa:
<input t:type="textField" t:id="password" t:validate="password" />
O mediante anotaciones:
@Validate("password") private String password;
Referencia:
http://tapestry.apache.org/forms-and-validation.html
http://tapestry.apache.org/bean-validation.html
Validaciones de datos de formularios con Apache Tapestry
Documentación sobre Apache Tapestry