How to check in a Validator if the value has been changed?

default-value, jsf, validation

Solution

The submitted value is just available as `value` argument in the `validate()` implementation.

public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
    Object oldValue = ((UIInput) component).getValue();

    if (value != null ? value.equals(oldValue) : oldValue == null) {
        // Value has not changed.
        return;
    }

    // Continue validation here.
}

An alternative is to design the `Validator` as a `ValueChangeListener`. It'll then only be invoked when the value has really changed. It's somewhat hacky, but it does the job you really need.

<h:inputText ... valueChangeListener="#{uniqueValueValidator}" />

or

<h:inputText ...>
    <f:valueChangeListener binding="#{uniqueValueValidator}" />
</h:inputText>

with

@ManagedBean
public class UniqueValueValidator implements ValueChangeListener {

    @Override
    public void processValueChange(ValueChangeEvent event) throws AbortProcessingException {
        FacesContext context = FacesContext.getCurrentInstance();
        UIInput input = (UIInput) event.getComponent();
        Object oldValue = event.getOldValue();
        Object newValue = event.getNewValue();

        // Validate newValue here against DB or something.
        // ...

        if (invalid) {
            input.setValid(false);
            context.validationFailed();
            context.addMessage(input.getClientId(context),
                new FacesMessage(FacesMessage.SEVERITY_ERROR, "Please enter unique value", null));
        }
    }

}

Note that you cannot throw a `ValidatorException` there, that's why need to manually set both the component and faces context to invalid and manually add the message for the component. The `context.validationFailed()` will force JSF to skip the update model values and invoke action phases.

Problem

I know I can get the old value through `UIInput#getValue()`. But in many cases where the field is bound to a bean value I want to get the default value that I don't need to validate if the input equals the default value. This is very usefull if a certain field has a unique constraint and you have a editing form. Validation will always fail since in a check constraint method it will always find its own value thus validating to false. One way would be to pass that default value as an attribute using `<f:attribute>` and check inside the validator. But is there an easier built-in way?

Original source