Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add Validators in Vaadin 8?

In Vaadin 7 there was an addValidator function, but in Vaadin 8 it does not exist.

Vaadin 7 Example:

   TextField user = new TextField("User:");
   user.setRequired(true);
   user.setInputPrompt("Your username");
   user.addValidator(new NullValidator("Username can't be empty", false));
   user.setInvalidAllowed(false);
like image 426
Diego D Avatar asked Feb 27 '17 17:02

Diego D


Video Answer


3 Answers

I found the answer here: Whats New

Example:

new Binder<Person>().forField(tf)
    .withValidator(str -> str.length() == 4, "Must be 4 chars")
    .withConverter(new StringToIntegerConverter("Must be Integer"))
    .withValidator(integer -> integer.equals(2017), "Wrong date")
    .bind(Person::getBirthYear, Person::setBirthYear);

enter image description here

like image 159
Diego D Avatar answered Oct 16 '22 19:10

Diego D


The accepted Answer by Diego D looks correct. That code looks to be taken from this very brief (3 minutes) but very helpful video published by the Vaadin company, Type safe validation before and after converters. Shows the new Vaadin 8 approach to validation. I'll add some notes, show expanded syntax, and provide full example code for a full working app.

Validator + Binder

One big difference in Vaadin 8 is that validators require the use of a binder. In the old days, you attached a validator to a field, but now in Vaadin 8 you attach a validator only to a binder. The Vaadin team recognizes that for some simple situations this requirement of a binder may prove annoying, but for the most part they sensibly expect situations needing validation are quite likely also doing binding. A very logical re-think, I believe. Discussed in another Vaadin company video, Webinar: What's new in Vaadin 8?.

Validation & converters

We define two different validators, one to be called before a converter converts the user’s data-entry and another to be called after conversion. So the order of the fluent-style withValidator and withConverter method calls is key to correct behavior here. Of course beforeConversion and afterConversion are poor names for validator objects, but are done so to make clear the intent of running before or after converter in this demo.

Lambda synax optional

One validator uses conventional Java code style overriding a method. The other validator uses Lambda syntax. Watch the video and see the Diego D Answer for code further simplified with single-line Lambda arguments.

package com.example.valapp;

import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.data.Binder;
import com.vaadin.data.ValidationResult;
import com.vaadin.data.Validator;
import com.vaadin.data.ValueContext;
import com.vaadin.data.converter.StringToIntegerConverter;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.*;

import javax.servlet.annotation.WebServlet;


/**
 * This UI is the application entry point. A UI may either represent a browser window
 * (or tab) or some part of a html page where a Vaadin application is embedded.
 * <p>
 * The UI is initialized using {@link #init(VaadinRequest)}. This method is intended to be
 * overridden to add component to the user interface and initialize non-component functionality.
 */
@Theme ( "mytheme" )
public class MyUI extends UI {

    @Override
    protected void init ( final VaadinRequest vaadinRequest ) {

        final TextField tf = new TextField ( "Enter year of birth:" );

        Validator<String> beforeConversion = new Validator < String > ( ) {
            @Override
            public ValidationResult apply ( String s, ValueContext valueContext ) {
               if(s.length ()!= 4) {
                   return  ValidationResult.error ( "Year must consist of 4 digits" );
               } else {
                   return  ValidationResult.ok () ;
               }
            }
        } ;

        Validator<Integer> afterConversion = Validator.from ( value -> value.equals ( 2017 ), "Wrong year." );

        new Binder < Person > ( )
                .forField ( tf )
                .withValidator ( beforeConversion )
                .withConverter ( new StringToIntegerConverter ( "Input must be Integer" ) )
                .withValidator ( afterConversion )
                .bind ( Person:: getYearOfBirth, Person:: setYearOfBirth );

        Button button = new Button ( "Tell me" );
        button.addClickListener ( event -> Notification.show("This is the caption", "This is the description", Notification.Type.HUMANIZED_MESSAGE) );

        setContent ( new VerticalLayout ( tf  , button ) );
    }

    @WebServlet ( urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true )
    @VaadinServletConfiguration ( ui = MyUI.class, productionMode = false )
    public static class MyUIServlet extends VaadinServlet {
    }
}
like image 27
Basil Bourque Avatar answered Oct 16 '22 21:10

Basil Bourque


What if you don't have a binder due to creating a dynamic form?

Vaadin 8.1 supports removing the binder for a field which supports dynamic forms. If you make a field invisible then remove the binder for that field. Re-add the binder when you make the field visible.

like image 34
Brett Sutton Avatar answered Oct 16 '22 19:10

Brett Sutton