DatePicker.setMin + Binder validation problem

Hello everyone!

I have the next use cases:

  • The user can input only dates in the future, so I’m using the setMin method.
  • user can load old objects (with old dates), component should be in invalid state.

Problem:
user opens old object → date component is invalid (but datePicker.isInvalid() returns false for some reason)
→ user changes date to valid value → component is still invalid.

What I’ve tried but it doesn’t help:

  • binder.setDefaultValidatorsEnabled(false);
  • datePicker.setManualValidation(true);

Without binder it works perfectly. Without DatePicker.setMin also. But I’d like to disable dates in the past.

Thanks for your attention.

@Route(value = TestView.ROUTE)
@PageTitle("Home")
@PermitAll
public class TestView extends VerticalLayout {

    private static final long serialVersionUID = 1L;
    public static final String ROUTE = "test";
    private DatePicker datePicker;
    private Binder<MyBean> binder;

    public TestView() {
	super();
	createContent();
	bind();
    }

    private void createContent() {
	datePicker = new DatePicker();
	datePicker.setMin(LocalDate.now());
	Button datePickerValidationBtn = new Button("Is datePicker valid", e -> {
	    Notification.show("datePicker is valid: " + !datePicker.isInvalid());
	});
	Button binderValidationBtn = new Button("Is binder valid", e -> {
	    Notification.show("binder is valid: " + binder.validate().isOk());
	});
	add(datePicker, datePickerValidationBtn, binderValidationBtn);
    }

    private void bind() {
	binder = new Binder<>();
	binder.forField(datePicker).bind(MyBean::getDate, MyBean::setDate);
	binder.setBean(new MyBean());
    }

    class MyBean {
	private LocalDate date = LocalDate.now().minusDays(3);

	public LocalDate getDate() {
	    return date;
	}

	public void setDate(LocalDate date) {
	    this.date = date;
	}
    }
}

Have you tried adding a validator for the binder?

something like

... .withValidator(value -> field.getMin().isBefore(value),"The value must be after ..."). ...

Binder hooks into the value change life cycle, tries to setup error handling and also overrides the component’s self managed validation state, but afaik it is not aware of field related specials, like a min or max value, and therefore it can come to such conflicts, where binder just relies on its own validators.

Yes, I have it in real code.
Without binder.setDefaultValidatorsEnabled(false); this validation won’t be even executed.

As I understand, firstly binder uses default validators (min, max), then binding-level validations.
When I open page binder doesn’t call validation immediately. But component is red already. I suppose that happens because of some inner component validation.

It’s red because you are setting a bean with an invalid value “date-3” when only today or newer is allowed.

Yes, I understand it. The problem here - component is still red after I change value to valid one.

Using this code I’ve disabled default validator.
image
But the component is still red.
Also I’ve debugged HasValidationProperties.setInvalid - we don’t come there with TRUE value.
Looks like component is red because of some client-side effect. Am I wrong?

For now as workaround I’m using binding.validate(). When I open page with outdated value, I choose valid one and datePicker is valid now.
But it has small side-effect: normally binder doesn’t trigger validation on entering the form (readBean)

@Route(value = TestView.ROUTE)
@PageTitle("Home")
@PermitAll
public class TestView extends VerticalLayout {

    private static final long serialVersionUID = 1L;
    public static final String ROUTE = "test";
    private DatePicker datePicker;
    private Binder<MyBean> binder;
    private MyBean bean;

    public TestView() {
	super();
	createContent();
	bind();
    }

    private void createContent() {
	datePicker = new DatePicker();
	datePicker.setMin(LocalDate.now());
	add(datePicker);
    }

    private void bind() {
	binder = new Binder<>();
	Binding<MyBean, LocalDate> bind = binder.forField(datePicker)
	.bind(MyBean::getDate, MyBean::setDate);
	bean = new MyBean();
	binder.readBean(bean);
	
	bind.validate();
    }

    class MyBean {
	private LocalDate date = LocalDate.now().minusDays(3);

	public LocalDate getDate() {
	    return date;
	}

	public void setDate(LocalDate date) {
	    this.date = date;
	}
    }
}