Fails to get invalid and null values from DateField for custom validation

Hi,

I am custom validating a DateField in a Form, while validating

  1. I should be able to get the invalid date value into my controller class, unfortunately null is returned if i enters non-date / invalid value into dateField under button click as well as under value change events. Here is the part of the validation for invalid date values,
    case.1 Value change event

    [code]
    @Override
    public void valueChange(ValueChangeEvent event) {

     			if (propertyId.equals(fieldName) && 
     					!event.getProperty().getValue().equals(Date.class) ) {
     				throwDateError(); // custom error message
     			}   
                               }
    

[/code]

case.2 Button click event
@Override public void buttonClick(final ClickEvent event) { if(!form.getField("fieldName").getValue().equals(Date.class)) { throwDateError(); // custom error message } }
I have tried with form.setInvalidAllowed(true); but when i fire button click to navigate to that form i get the following exception

Caused by: java.lang.UnsupportedOperationException
	at com.vaadin.ui.Form.setInvalidAllowed(Form.java:1027) [:6.8.0.nightly-20111122-c22085]
  1. I should be able to check for null for custom validation of required DateFields inside the form, i tried to check for field.isEmpty() but no luck because isEmpty() is a protected boolean in Form.class.
    Please aid me to check for invalid as well as null values from the DateField in order to do differentiate between custom required validation and custom invalid date value validations. Any help will be appreciated in advance.
    thank you,
    Rakesh

Hi,
to your second question: try field.getValue() == null instead of field.isEmpty() , another possibility could be field.setRequired(true) .

[e]
: I saw, in you first question you set invalidAllowed onto the form, but I think you should set in on the field instead! You also should use field.setImmediate(true), so an error will be indicated immediately on the gui.

Hi,

I have investigated for DateField in the form, if user enters invalid date / non date or string value, field.getValue() always return null which prohibits me in differentiate between validation for null or required field and validation for non date value inputs.

Anyhow i have used this but while displaying i should display instant / immediate custom error messages in header of the application instead of displaying error message in form error component.
Please suggest anymore way of getting values to my controller for custom validation.

Waiting for reply,
Rakesh

Take a look at DateField.handleUnparsableDateString(…). If it throws an exception, it is internally converted to a flag value in DateField.changeVariables(…) and used to throw another exception when DateField.validate() is called, so I believe you should get a validation exception in this case.

Maybe someone else can give a better answer for this one, but as starting points you could take a look at AbstractComponent.setComponentError(…), AbstractField.setValidationVisible(…) and AbstractField.getErrorMessage().

I’m not sure if you mean “immediate” in that an error shows up as soon as focus leaves the field, but if so you can probably use the below but just set the form or fields to immediate mode.

This works for us to provide feedback in a notification instead of inside the form. First, I hide the little red asterisks next to the required fields with this bit of css:

.v-required-field-indicator {
    visibility: hidden;
}

Then we create a button for submitting the form and attach a listener. In the listener, we do this (more or less):

    public void buttonClick(ClickEvent event) {
        try {
            form.validate();
        } catch (InvalidValueException ive) {
            getWindow().showNotification(
                processInvalidValueMessage(ive));
            return;
        }
        // process the data source of the form
    }

I’m pulling this together from various pieces of the real code, so the above may not be exactly right (e.g. you may want to do form.commit instead of validate in the try block, which I think performs validation at that time). The point is that we don’t create the standard button with the form.commit listener, but use our own listener instead so that we can intercept the exceptions. It’s a very small amount of code. :slight_smile:

The “processInvalidValueMessage” method above pulls the message out of the exception. This is because InvalidValueException handles messages in an unusual way since it can contain an array of causes if a field failed more than one validator. Here’s my version of that method (more or less):

    public String processInvalidValueMessage(InvalidValueException exception) {
        InvalidValueException[] causes = exception.getCauses();
        if (causes == null || causes.length == 0) {
            return exception.getLocalizedMessage();
        }

        // I don't know if these ever nest more than two deep, but just in case
        return getInvalidValueMessage(causes[0]
);
    }

I think I’ve mentioned this way of handling invalid value exceptions before; if so, sorry for repeating myself.

Cheers,
Bobby

Hi

The Vaadin 7.1 DateField seems to have issues related to this thread. The problem is in the changeVariables method. As you can see below in the code snippet, in case of error in the user input parsing the setValue(null) is called before currentParseErrorMessage and uiHasValidDateString fields are set. This causes problems because setValue invokes the value change event which in turn can be used to do real time operations on the field right after the value is changed and you usually want to check first if value is valid. The validation() method does not work correctly because at the time of the value change event the uiHasValidDateString has not been changed nor the currentParseErrorMessage and no validation exception is thrown. As it is currently implemented there is no way to deduce whether the value was just empty or if there was parsing error at the time of value change event.

The processInvalidValueMessage method can not be used to solve this as the currentParseErrorMessage and uiHasValidDateString are private.

It looks to me that it would be trivial to fix this by setting the variables in question before the if block.

Best regards,
Tommi Laukkanen

                } catch (Converter.ConversionException e) {

                    /*
                     * Datefield now contains some text that could't be parsed
                     * into date.
                     */
                    if (oldDate != null) {
                        /*
                         * Set the logic value to null.
                         */
                        setValue(null);
                        /*
                         * Reset the dateString (overridden to null by setValue)
                         */
                        dateString = newDateString;
                    }

                    /*
                     * Saves the localized message of parse error. This can be
                     * overridden in handleUnparsableDateString. The message
                     * will later be used to show a validation error.
                     */
                    currentParseErrorMessage = e.getLocalizedMessage();

                    /*
                     * The value of the DateField should be null if an invalid
                     * value has been given. Not using setValue() since we do
                     * not want to cause the client side value to change.
                     */
                    uiHasValidDateString = false;

The Book of Vaadin has an example here of how to set Date Validation:
https://vaadin.com/book/-/page/components.datefield.html
which actually exposes a subtle Java Date item which is annoying.

So for example 13/01/2000 in a
MM/dd/YYYY
format would be rounded up to 01/01/2001.

Below is a simple way to prevent your date from getting rounded.
[code]
static final String DATE_FORMAT = “MM/dd/yyyy”;

protected Date handleUnparsableDateString(String dateString) throws Converter.ConversionException {
    DateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
    try {
        Date d = sdf.parse(dateString);
        if (sdf.format(d).equals(dateString)) {
            // String.equals() detects an issue were values are rounded up.
            // So for example 13/01/2000 would be rounded up to 01/01/2001 without this simple check.                
            return d;
        } else {
            // throw an error that the date is invalid
            throw new Converter.ConversionException("The value " + dateString + " is an invalid date, enter a date in the form MM/DD/YYYY");
        }
    } catch (ParseException e) {
        throw new Converter.ConversionException("The value " + dateString + " is an invalid date, enter a date in the form MM/DD/YYYY");
    }

[/code]