How do I validate a DateField correctly?

Hi,

I’m having some trouble with validating DateFields.
In my application I have a table with DateField properties that the users should be able to edit by pressing an edit button. I also have an OK-button that commits the fields and a cancel-button that discards them.


Here’s what I want to achieve
(of course there are some neat rules that has to be followed):

  • First off, dates can only be changed to the current day up to 9999-12-31.
  • Secondly, I preferably want the validation to be dynamic (as-you-type)
  • The original dates (dates that are already in the table when going into editing mode) can be any date and you should be able to commit these as they are, even if they are in the past.
  • If you change the date to an invalid date (you can still do this “manually”, i.e. directly in the field, not in the date picker) or enter an invalid character into the DateField, an error icon should be shown with a message, not letting you commit your changes until you entered a valid date.
  • If you change the date to an invalid date (error icon is shown), and then to a valid date, the error icon should disappear.

The
current behaviour
that I managed to implement does the following:

  • Allows “original dates” - OK
  • Allows changing to a valid date - OK
  • When changing to an invalid date (can be done “manually”, not using the date picker) and press enter in the field, the field is immediately reset to the original date, but the error icon is still shown - NOT ok
  • When entering an invalid character (can be done “manually”, not using the date picker) and then press Ok (commit()) in the field, a NPE will be thrown, also no error icon is shown - NOT ok
  • When changing to an invalid date and press enter in the field and then back to a valid date and press enter in the field, the error icon is still there - NOT ok
  • When changing to an invalid date and press OK (i.e. commit()), the field first resets to the original date and the changes (i.e. no change at all to the field) are committed - NOT ok

Now, I tried implementing a wrapper so that I could listen for value changes, but DateField don’t have convenient methods like TextField has (eg. setTextChangeEventMode and setTextChangeTimeout). I override valueChange to take care of some of the issues, but it only seems gets called when you change to a valid date, not when you change to an invalid date (you also have to press enter every time when not using the date picker)… instead another validate() gets called in the background, resetting setValidationVisible() all the time.

I even tried creating a CustomDateRangeValidator, but found out it wasn’t to much help.

Please help me get this straight, I have tried so many things now and I’m running out of options.


Here’s my createField method:

createField(){
    // some more code up here...

    if (propertyId.equals("Valid From")) {
        dField.setImmediate(true);

        dField.setRangeStart(new Date());
        dField.setRangeEnd(dateUtil.getDate(9999, 12, 31));
        dField.setDateOutOfRangeMessage("Date out of range!");

        @SuppressWarnings({ "unchecked", "rawtypes" })
        TableDataValidatingWrapper<TextField> wField = new TableDataValidatingWrapper(dField);
        return wField;
    }

    // some more code down here...
}


… and here’s my wrapper:

public class TableDataValidatingWrapper<T> extends CustomField<T> {

    private static final long serialVersionUID = 1L;
    protected Field<T> delegate;

    public TableDataValidatingWrapper(final Field<T> delegate) {
        this.delegate = delegate;

        if (delegate instanceof DateField) {
            final DateField dateField = (DateField) delegate;

            dateField.setCaption("");
            dateField.setImmediate(true);
            dateField.setInvalidAllowed(false);
            dateField.setInvalidCommitted(true);
            dateField.setValidationVisible(false);
            dateField.addValueChangeListener(new ValueChangeListener() {

                private static final long serialVersionUID = 1L;

                @Override
                public void valueChange(com.vaadin.data.Property.ValueChangeEvent event) {
                    try {
                        dateField.validate();
                        dateField.setValidationVisible(false);
                    } catch (InvalidValueException ive) {
                        //handle exception
                    } catch (Exception e) {
                        //handle exception
                    }
                }
            });

        }
    }

//some other overridden methods here...
}