I ran into similar issue with Vaadin, especially when trying to run on the fly validation (without commiting) and when wanting to display field contextual errors. The issue is that when a field changes, its validator is called, but not the validators of the other fields. With cross field validation, a field could be validated/unvalidated when another field changes.
So, a solution is to manipulate the field's "componentError" member. In this example, the validation consists in ensuring that at least one of the two fields is provided:
final Validator atLeastOneFieldValidator = new Validator() {
@Override
public void validate(final Object value) throws InvalidValueException {
if (!crossValidate()) {
throw new InvalidValueException("");
}
}
};
field1.addValidator(atLeastOneFieldValidator);
field2.addValidator(atLeastOneFieldValidator);
My "crossValidate" method:
private boolean crossValidate() {
if (Strings.isNullOrEmpty(field1.getValue()) && Strings.isNullOrEmpty(field2.getValue())) {
final String error = "At least 1 field must be filled";
field1.setComponentError(new UserError(error));
field2.setComponentError(new UserError(error));
return false;
}
field1.setComponentError(null);
field2.setComponentError(null);
return true;
}
But unfortunately this is not enough (and I cannot explain why), because when my two fields appear in error and I fill one of them, the error should disappear on both of them... but it only disappears from the one I modified. Maybe it's a bug in Vaadin. So I had to create a ValueChangeListener on the same model, in addition to the validator:
final Property.ValueChangeListener atLeastOneField = new Property.ValueChangeListener() {
@Override
public void valueChange(final Property.ValueChangeEvent event) {
crossValidate();
}
};
field1.addValueChangeListener(atLeastOneField);
field2.addValueChangeListener(atLeastOneField);
I don't know if there's better ways to do it, because it's full of workarounds. If someone has a better answer I would also like to see it! I post it as an answer although it raises new question.