Validación Vaadin Spring Boot

Tercer tutorial de la serie Vaadin Spring Boot, en esta ocasión estará dedicado a la validación de los datos de entrada por medio de formularios, este framework nos proporciona una serie de clases para dicho propósito, también es posible crear nuestras propias implementaciones.

Anteriormente aprendimos a crear un formulario, usaremos ese ejemplo como base, solo añadiremos los componentes de validación a dicho proyecto.

Vaadin Spring Boot Validación de formularios

Para este proyecto demostrativo usaremos en siguiente modelo de datos:

public class Person {

    private String name;
    private Integer age;
    private String email;
    private String pass;
    private String provincia;

    // ...
}

Ahora podemos crear un formulario para dicho modelo, vamos a extender la clase FormLayout y agregaremos los elementos de la UI requeridos para cada campo.

public class PersonForm extends FormLayout {

    Binder<Person> binder = new Binder<>(Person.class);

    TextField name = new TextField("Nombre");
    TextField email = new TextField("Correo");
    PasswordField pass = new PasswordField("Contraseña");
    Slider edad = new Slider("Edad");
    NativeSelect<String> provincia = new NativeSelect<>("Residencia");

    Person person;

    public PersonForm() { ... }

    private void initUI() {
        
        name.setPlaceholder("Escribe tu nombre");
        name.setWidth("100%");
        name.setIcon(VaadinIcons.USER);

        email.setPlaceholder("Escribe tu correo");
        email.setWidth("100%");
        email.setIcon(VaadinIcons.ENVELOPE);

        pass.setPlaceholder("Escribe tu password");
        pass.setWidth("100%");
        pass.setIcon(VaadinIcons.PASSWORD);

        edad.setWidth("100%");
        edad.setIcon(VaadinIcons.CALENDAR_USER);
        
        provincia.setItems("Panama", "Veraguas", "Colon", "Herrera", "otro.");
        provincia.setIcon(VaadinIcons.HOME);
        
        this.addComponents(name, email, pass, edad, provincia);
    }

    private void initBinder() { ... }

    private void initBean() { ... }

}

Podemos ver que agregamos dos controles TextField uno para el nombre y otro para el correo electrónico, un control PasswordField para la contraseña, un Slider para ingresar la edad de la persona y un control NativeSelect que le permitirá al usuario seleccionar el lugar de residencia en Panamá.

Usamos el método setPlaceHolder("...") para definir el texto que muestra el control cuando no ha sido editado, setWidth("100%") indica que dicho control debe ocupar el 100% de el espacio disponible, además setIcon(...) establece el icono que muestra la etiqueta del formulario, simplemente seleccionamos el deseado.

public class PersonForm extends FormLayout {

    Binder<Person> binder = new Binder<>(Person.class);

    // ...

    private void initBinder() {
        
        binder.forField(edad)
                .withValidator(age -> age >= 18, "Debes ser mayor de edad.")
                .withConverter(Double::intValue, Integer::doubleValue)
                .bind(Person::getAge, Person::setAge);

        binder.forField(name)
                .asRequired("Debes indicar el nombre")
                .bind("name");

        binder.forField(email)
                .withValidator(new EmailValidator("Este correo es incorrecto."))
                .bind("email");

        binder.forField(pass)
                .asRequired("Debes crear una contraseña")
                .withValidator(new PersonPassValidator())
                .bind("pass");

        binder.forField(provincia)
                .asRequired("Cual es tu lugar de residencia")
                .bind(Person::getProvincia, Person::setProvincia);
    }

    // ...

}

Para enlazar o sincronizar los campos del formulario con el objeto Person correspondiente debemos crear un Binder<Person>, usando el método forField() indicamos el campo del formulario que sincronizaremos, con bind() indicamos la propiedad del objeto Person a la que se enlaza dicho campo, podemos simplemente indicar el nombre de la propiedad o establecer los setter y getter que utilizaremos, en el ejemplo se usan las dos opciones.

Usaremos asRequiered("...") para indicar que es obligatorio establecer este campo, debemos indicar el mensaje de error que se muestra cuando se detecta este error, al ubicar el cursor del ratón sobre el icono de error rojo se muestra dicho mensaje.

binder.forField(name)
        .asRequired("Debes indicar el nombre")
        .bind("name");

Con withValidatior() indicamos el validador asignado al campo, puede ser uno de los proporcionados por el framework, por ejemplo, EmailValidator que valida una dirección de correo electrónico.

binder.forField(email)
        .withValidator(new EmailValidator("Este correo es incorrecto.")
        .bind("email");

Usando las expresiones lambdas en fácil definir validadores, este ejemplo muestra error si la edad ingresada mediante el control es menor que 18, este requiere adicionalmente un convertidor para solo debemos indicar una función que convierta los datos de Double a Integer y otra que haga lo contrario.

binder.forField(edad)
        .withValidator(age -> age >= 18, "Debes ser mayor de edad.")
        .withConverter(Double::intValue, Integer::doubleValue)
        .bind(Person::getAge, Person::setAge);

El tema de los convertidores será tratado con más detalle en futuros tutoriales.

Otra forma es implementar la interface Validator, nuestro ejemplo crea una validación para la contraseña, para que la misma sea considerada correcta debe tener un mínimo de 8 caracteres, y debe contener por lo menor una letra y un número.

public class PersonPassValidator implements Validator<String> {

    @Override
    public ValidationResult apply(String value, ValueContext context) {
        if (value.trim().length() >= 8) {
            
            boolean digit = false;
            boolean letter = false;
            
            for (int i = 0; i < value.length(); i++) {
                digit |= Character.isDigit(value.charAt(i));
                letter |= Character.isLetter(value.charAt(i));
            }
            
            if(digit && letter){
                return ValidationResult.ok();
            }else {
                return ValidationResult.error("La contraseña debe contener letras y numeros.");
            }
        } else {
            return ValidationResult.error("La contraseña debe tener un minimo de 8 caracteres.");
        }
    }

}

Sobre escribimos el método apply() la variable value contiene el texto introducido en el formulario, verificamos si cumple con nuestras condiciones, de ser así devolvemos ValidationResult.ok() en caso contrario ValidationResult.error("..."), agregamos este validador de la siguiente manera:

binder.forField(pass)
        .asRequired("Debes crear una contraseña")
        .withValidator(new PersonPassValidator())
        .bind("pass");

Podemos observar que es posible agregar más de un validador.

Para finalizar solo nos falta agregar la @SpringUI para este formulario, igual que lo hicimos en tutoriales anteriores, tenemos:

@SpringUI(path = "/person")
public class PersonUI extends UI {

    @Override
    protected void init(VaadinRequest request) {

        Label lbl = new Label("<h1>Spring Boot Vaadin Validation</h1>", ContentMode.HTML);
        
        PersonForm form = new PersonForm();

        VerticalLayout root = new VerticalLayout(lbl, form);

        setContent(root);
    }
}

Si ejecutamos y visitamos la URL http://localhost:8080/person 

Validar formulario usando Vaadin y Spring Boot

Para finalizar mostraremos como podemos saber si el formulario tiene errores, esto será útil si por ejemplo tenemos un botón para guardar en una base de datos, antes de guardar debemos saber si hay errores, para esta demostración agregamos un Button a la UI, el método que responde al clic es el siguiente:

private void validate(Button.ClickEvent event) {

    BinderValidationStatus<Person> status = binder.validate();
    
    if (status.hasErrors()) {
        Notification.show(String.format(
                "Se han encontrado %d errores.",
                + status.getValidationErrors().size()));
    }
}

Ejecutamos el método binder.validate() para realizar la validación, luego comprobamos con status.hasErrors() si existen errores, podemos obtener los errores si lo deseamos, en nuestro caso solo los contamos y mostramos el mensaje.

Es todo por ahora, nos vemos en la próxima.

Descargar proyecto: validación-vaadin-springboot.zip

Comentarios

Entradas populares de este blog

Conectar SQL Server con Java

Entrenar OpenCV en Detección de Objetos

Procesamiento de imágenes en OpenCV

Acceso a la webcam con OpenCV

Conociendo la clase cv::Mat de OpenCV