Search code examples
vaadin8

Disable a Vaadin Binding


I have a component (e.g. a TextField) that will be shown depending on some other selection, a radio button for example, and when it is hidden I don't want the bind on this field to be applied, so

In some place:

Binder binder = new Binder<SomeDTO>(SomeDTO.class);
TextField conditionalComponet = TextField("A conditional component: ");
binder.bind(conditionalComponet, "propertyX");

and in some other place:

SomeDTO someDTO = new SomeDTO;
binder.writeBean(someDTO); //Here propertyX shouldn't be filled, i.e. bind should 
                       //not be applied, to propertyX of SomeDTO if the 
                       //conditionalComponet is hidden.

I wouldn't like to remove the component from layout since putting it in the same position will be a problem. I tried to setVisible(false), setEnabled(false) and setReadOnly(true), but none prevent the binding to be applied. Is there a simple way of doing that?

I am using Vaadin 8.


Solution

  • As far as I know there's no direct way to prevent a value from being set to a field by the binder, once it's bound. Nonetheless, you can work around this limitation(?!) easily, by using the bind(HasValue<FIELDVALUE> field, ValueProvider<BEAN,FIELDVALUE> getter, Setter<BEAN,FIELDVALUE> setter) method variant.

    For a sample you can check the code below. Please note that it has just the basics parts to show the effect, so it does not have all the bells-and-whistles such as disabling or resetting the employment-date field with the check-box:

    import com.vaadin.data.Binder;
    import com.vaadin.data.ValidationException;
    import com.vaadin.ui.*;
    
    import java.io.Serializable;
    import java.time.LocalDate;
    
    public class DisableBindingForField extends VerticalLayout {
        private final Person person = new Person("Dark Vaper");
        private final TextField name = new TextField("Name:");
        private final CheckBox isEmployed = new CheckBox("Is employed:");
        private final DateField dateOfEmployment = new DateField("Date of employment:");
        private final Binder<Person> binder = new Binder<>(Person.class);
        private final Button button = new Button("Save");
    
        public DisableBindingForField() {
            // manually bind the custom field separately (https://vaadin.com/docs/-/part/framework/datamodel/datamodel-forms.html - scroll to the end)
            binder.bind(dateOfEmployment, person -> person.dateOfEmployment, (person, dateOfEmployment) ->
                    // if the check-box is checked then populate the pojo with the value, otherwise reset the pojo value to null
                    person.setDateOfEmployment((isEmployed.getValue()) ? dateOfEmployment : null)
            );
    
            // automatically bind the rest of the fields
            binder.bindInstanceFields(this);
    
            // initial reading of the bean
            binder.readBean(person);
    
            // add components to the user interface
            addComponents(name, isEmployed, dateOfEmployment, button);
    
            // simulate a "save button"
            button.addClickListener(event -> {
                try {
                    binder.writeBean(person);
                    Notification.show(person.toString());
                } catch (ValidationException e) {
                    e.printStackTrace();
                }
            });
        }
    
        // basic pojo for binding
        public class Person implements Serializable {
    
            private String name;
            private Boolean isEmployed;
            private LocalDate dateOfEmployment;
    
            public Person(final String name) {
                this.name = name;
            }
    
            public String getName() {
                return name;
            }
    
            public void setName(final String name) {
                this.name = name;
            }
    
            public boolean isEmployed() {
                return isEmployed;
            }
    
            public void setEmployed(boolean employed) {
                isEmployed = employed;
            }
    
            public LocalDate getDateOfEmployment() {
                return dateOfEmployment;
            }
    
            public void setDateOfEmployment(LocalDate dateOfEmployment) {
                this.dateOfEmployment = dateOfEmployment;
            }
    
            @Override
            public String toString() {
                return "Person{" +
                        "name='" + name + '\'' +
                        ", isEmployed=" + isEmployed +
                        ", dateOfEmployment=" + dateOfEmployment +
                        '}';
            }
        }
    }
    

    Result:

    ignore employment date binding if check-box is not checked

    P.S.: Alternative fluent-builder style:

    binder.forField(dateOfEmployment)
          .bind(person -> person.dateOfEmployment, (person, dateOfEmployment) ->
              // if the check-box is checked then populate the pojo with the value, otherwise reset the pojo value to null
              person.setDateOfEmployment((isEmployed.getValue()) ? dateOfEmployment : null)
          );