Validation Woes.

Hi all.

Im still relatively new to vaadin, and Ive been struggling to get field validation working as i want it… Which almost certainly means im doing it wrong.

In our app we have a view where several of the fields are semantically linked to each other. They are either all valid, or all invalid, depending on the individual values entered into each field. I would like to mark all fields as invalid as soon as one of the fields has a value entered that makes the group invalid. Also the group can become valid again by changing a value in a different field.

The simplest Example i can think of is a number range with a start and end value. if start > end i want to mark both fields invalid. the user can correct the range by either making start less than end, or by making end greater than start. at which point both fields should be marked as valid.

It is fairly simple to use a custom Validator to check if an individual field should be marked invalid, with something like

    start = new TextField();
    start.setImmediate(true);
    start.addValidator(new Validator() {
        @Override
        public void validate(Object value) throws InvalidValueException {
            if (!validRange((String) value, end.getValue())) {
                throw new InvalidValueException("Invalid Range.");
            }
        }
    });

    end = new TextField();
    end.setImmediate(true);
    end.addValidator(new Validator() {
        @Override
        public void validate(Object value) throws InvalidValueException {
            if (!validRange(start.getValue(), (String) value)) {
                throw new InvalidValueException("Invalid Range.");
            }
        }
    });

I am struggling with triggering the validation of the other fields, and having their valid / invalid state updated in the display.

for example above if you enter 5 in start and then 3 in end, then the end field is marked invalid but the start is not. then modifying start to 1 doesnt mark end as valid again… Clearly this is because there is no coded link between them to indicate that the other field should also have its validation performed.

calling validate() on the other fields doesnt work, and leads to a stack overflow as you descend into recursion hell. I dont see anyway to trigger an event or some other action to cause the validation to be run for each field in the group.

What i was considering was some sort of extension that groups the fields together into a validation group that uses client side code to trigger the validation call for each field in the group when any of them are changed. This would then leave the validators relatively simple… unless there is a better server side solution to this.

Surely someone else has already come up with a solution to this.

Hi,

how about using a FieldGroup and calling validate on that? It should go through all the fields bound to it and validate each one.

-tepi

Hi Tepi,

Do you mean using a FieldGroup to bind the Fields to a Bean, and then using bean validation to validate the bean? I dont see a validate method directly on the FieldGroup itself.

The values in our various fields are interdependant. If i understand correctly from this blog entry https://vaadin.com/web/matti/blog/-/blogs/cross-field-validation-in-vaadin#oilr_message_10236465 cross field validation for beans isnt really supported yet in Vaadin. Although I will have a closer look at Viritin.

Ive also found this thread https://vaadin.com/forum/#!/thread/330329 which seems to suggest a solution like this for the example above.

    start = new TextField();
    start.setImmediate(true);
    start.addValidator(new Validator() {
        @Override
        public void validate(Object value) throws InvalidValueException {
            if (!validRange((String) value, end.getValue())) {
                throw new InvalidValueException("Invalid Range.");
            }
        }
    });

    end = new TextField();
    end.setImmediate(true);
    end.addValidator(new Validator() {
        @Override
        public void validate(Object value) throws InvalidValueException {
            if (!validRange(start.getValue(), (String) value)) {
                throw new InvalidValueException("Invalid Range.");
            }
        }
    });
    
    final ValueChangeListener changeValueListener = new ValueChangeListener() {
        @Override
        public void valueChange(ValueChangeEvent event) {
            try {
                start.validate();
                start.setComponentError(null);
            } catch (Exception e) {
                start.markAsDirty();
            }
            try {
                end.validate();
                end.setComponentError(null);
            } catch (Exception e) {
                end.markAsDirty();
            }
        }
    };
    start.addValueChangeListener(changeValueListener);
    end.addValueChangeListener(changeValueListener);

which seems to work well enough for our needs in a 2 value group like the range example that i will try to extend to our specific case.