Search code examples
data-bindingvaadin8

Vaadin's Binder#hasChanges() returns falsely 'false'


I'm using the binder.addStatusChangeListener( ... ) part of the code at Binding Data to Forms, Checking a return value in my webapp. MCVE derived from that is:

Person Bean

public class Person implements Serializable {

    private String name;

    public Person() {}

    public Person( final String name ) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName( final String name ) {
        this.name = name;
    }
}

Person View

public class PersonView extends VerticalLayout {

    private final Person person = new Person( "Gerold Broser" );
    private final TextField name = new TextField( "Name:" );
    private final BeanValidationBinder<Person> binder =
            new BeanValidationBinder<>( Person.class );
    private final Button button = new Button( "Button" );

    public PersonView() {
        super();

        binder.bindInstanceFields( this );
        binder.setBean( person );

        addComponent( name );
        addComponent( button );

        binder.addStatusChangeListener( event -> {
            final boolean hasChanges, isValid;
            out.println( "hasChanges=" + (hasChanges = event.getBinder().hasChanges()) );
            out.println( "isValid=" + (isValid = event.getBinder().isValid()) );
            button.setEnabled( hasChanges && isValid );
        } );
    }
}

As soon as I change the TextField's content in my browser I get the following output:

hasChanges=false
isValid=true

Is this a bug or do I miss something?


Solution

  • From the javadoc:

    public boolean hasChanges()

    Check whether any of the bound fields' have uncommitted changes since last explicit call to readBean(Object), removeBean(), writeBean(Object) or writeBeanIfValid(Object). Unsuccessful writeoperations will not affect this value.

    Note that if you use setBean(Object) method, Binder tries to commit changes as soon as all validators have passed. Thus, when using this method with it seldom makes sense and almost always returns false.

    Return values for each case are compiled into the following table:

    ╔════════════╦════════════╦═════════╦═════════╦══════════════════╦════════════════════╗
    ║            ║ After      ║ After   ║ After   ║ After successful ║ After unsuccessful ║
    ║            ║ readBean,  ║ valid   ║ invalid ║ writeBean or     ║ writeBean or       ║
    ║            ║ setBean or ║ user    ║ user    ║ writeBeanIfValid ║ writeBeanIfValid   ║
    ║            ║ removeBean ║ changes ║ changes ║                  ║                    ║
    ╠════════════╬════════════╬═════════╬═════════╬══════════════════╬════════════════════╣
    ║ A bean is  ║            ║         ║         ║                  ║                    ║
    ║ currently  ║   false    ║  false  ║  true   ║      false       ║     no change      ║
    ║ bound      ║            ║         ║         ║                  ║                    ║
    ╟────────────╫────────────╫─────────╫─────────╫──────────────────╫────────────────────╢
    ║ No bean is ║            ║         ║         ║                  ║                    ║
    ║ currently  ║   false    ║  true   ║  true   ║      false       ║     no change      ║
    ║ bound      ║            ║         ║         ║                  ║                    ║
    ╚════════════╩════════════╩═════════╩═════════╩══════════════════╩════════════════════╝
    

    Returns: whether any bound field's value has changed since last call to setBean, readBean, writeBean or writeBeanIfValid

    In conclusion, using setBean(myBean) will trigger automatic commits. Thus, if you want to manually commit changes, use binder.readBean(myBean)& binder.writeBean(myBean).