且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

如何确定 DateField 在 Vaadin 8 中是否有效

更新时间:2023-01-25 18:52:50

This has been asked several times, and as far as I know it's not possible to add validators without the binder. You can check this answer for a comprehensive description of the feature and resoning.

There are also discussions on the Vaadin forum and github, and the 2 main suggestions are using a binder, or a value change listener where you manually call a validator. However, the latter solution does not seem to work and I suspect that's because a value change event is triggerd only when the actual value changes, which probably does not happen when you type something invalid, but I did not spend much time investigating.

The latest suggestion on github requests a binder.noBind() method to facilitate these cases, but until that is implemented you could use something similar to the below code sample. I also hated the idea of using a field to bind the value to, so I went with a no setter & no getter concept:

public class DateFieldWithValidator extends VerticalLayout {

    public DateFieldWithValidator() {
        // date field with binder
        Binder<LocalDate> binder = new Binder<>();
        DateField dateField = new DateField("Date");
        binder.forField(dateField)
              .asRequired("Please select a date")
              .bind(No.getter(), No.setter());

        // validity status
        TextField validityField = new TextField("Status:", "N/A");
        validityField.setReadOnly(true);
        validityField.addStyleName(ValoTheme.TEXTFIELD_BORDERLESS);
        validityField.setWidth("100%");

        // submit button
        Button submitButton = new Button("Submit");
        submitButton.addClickListener(event -> {
            BinderValidationStatus<LocalDate> status = binder.validate();
            if (status.isOk()) {
                validityField.setValue("OK: " + dateField.getValue().toString());
            } else {
                validityField.setValue("KO: " + status.getValidationErrors().stream().map(ValidationResult::getErrorMessage).collect(Collectors.joining(",")));
            }
        });

        addComponents(dateField, submitButton, validityField);
    }

    // convenience empty getter and setter implementation for better readability
    public static class No {
        public static <SOURCE, TARGET> ValueProvider<SOURCE, TARGET> getter() {
            return source -> null;
        }

        public static <BEAN, FIELDVALUE> Setter<BEAN, FIELDVALUE> setter() {
            return (bean, fieldValue) -> {
                //no op
            };
        }
    }
}

Result:


Later update:

I've been thinking some more, and if it's acceptable, you could disable the submit button if the value is null or a parsing error occurs for an invalid value. This can be easily implemented with a ValueChangeListener and an ErrorHandler like below.

Alternatively, you can save the exception message in a variable, and when you click the button check if either there is an error message or the value is null, in this order because if you input an invalid date, the field's value will be set to null.

public class DateFieldWithValidator extends VerticalLayout {
    public DateFieldWithValidator() {
        DateField dateField = new DateField("Date");
        Button submitButton = new Button("Submit");
        submitButton.setEnabled(false);
        submitButton.addClickListener(event -> Notification.show("Selected date: " + dateField.getValue()));
        dateField.setRequiredIndicatorVisible(true);
        dateField.setErrorHandler(event -> submitButton.setEnabled(false));
        dateField.addValueChangeListener(event -> submitButton.setEnabled(event.getValue() != null));
        addComponents(dateField, submitButton);
    }
}

Result: