[Solution] Validate no duplicate entries in datatable

UI Components for JSF
Post Reply
porthos
Posts: 122
Joined: 09 May 2011, 03:17

10 Apr 2012, 03:23

I've seen some recent discussion on validating that no duplicate values have been entered into input components in a datatable, so I thought I'd leave our solution here as it might be useful to others.

The solution should cover a fair number of regular use cases for input components in any UIData based component. It provides a usability benefit by using the standard JSF validation mechanism, so that inputs containing duplicate values are individually highlighted as errors to users, and can have <h:message/> attached per input to provide contextual help for the error.

But it only validates values passed during a single request, so it won't work against duplicate values in pages of a datatable that are not being processed in a given request/response cycle.

Here's what it looks like used in a facelet:

Code: Select all

    <h:dataTable var="emailEntry" value="#{bean.emailList}">

      <h:column headerText="#{i18n['EMAIL_ADDRESSES_HEAD']}">

        <p:inputText id="email" value="#{emailEntry}">
          <f:validator validatorId="noDuplicates"/>
          <f:attribute name="duplicatesMessage" value="#{i18n['ERR_DUPLICATE_EMAIL']}"/>
        </p:inputText>

        <p:message for="email"/>

      </h:column>

    </h:dataTable>
And here's the Validator code (divided into an abstract and a concrete class).

Code: Select all

public abstract class MessageAttributeAwareValidator implements Validator {

    private final String messageAttributeName;
    private final String defaultMessage;

    protected MessageAttributeAwareValidator(final String messageAttributeName, final String defaultMessage) {
        this.messageAttributeName = messageAttributeName;
        this.defaultMessage = defaultMessage;
    }


    protected void failValidation(final UIComponent component) {
        String message = (String) component.getAttributes().get(messageAttributeName);
        message = message == null ? defaultMessage : message;
        throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, message, message));
    }
}

Code: Select all

@FacesValidator(value = "noDuplicates")
public class NoDuplicatesValidator extends MessageAttributeAwareValidator {

    public static final String REQUEST_SCOPE_KEY = "_NoDuplicatesValidatorValuesMap";
    public static final String DUPLICATES_MESSAGE = "duplicatesMessage";
    public static final String DEFAULT_MESSAGE = "Duplicate Value";

    public NoDuplicatesValidator() {
        super(DUPLICATES_MESSAGE, DEFAULT_MESSAGE);
    }

    @Override
    public void validate(final FacesContext context, final UIComponent component, final Object value)
            throws ValidatorException {

        final UIInput input = (UIInput) component;
        final Set<Object> componentValues = getComponentValuesSet(context, input);

        if (componentValues.contains(value)) {
            failValidation(component);
        }

        componentValues.add(value);
    }

    private static Set<Object> getComponentValuesSet(final FacesContext context, final UIInput input) {
        final Map<String, Object> requestScope = getInitialisedRequestScope(context);
        final Map<UIInput, Set<Object>> valuesMap =
                (Map<UIInput, Set<Object>>) requestScope.get(REQUEST_SCOPE_KEY);
        if (null==valuesMap.get(input)) {
            valuesMap.put(input, new HashSet<Object>());
        }
        return valuesMap.get(input);
    }

    private static Map<String, Object> getInitialisedRequestScope(final FacesContext context) {
        final Map<String, Object> requestScope = context.getExternalContext().getRequestMap();
        if (!requestScope.containsKey(REQUEST_SCOPE_KEY)) {
            requestScope.put(REQUEST_SCOPE_KEY, new HashMap<UIInput, Set<Object>>());
        }
        return requestScope;
    }
}

None of this is primefaces specific though - so might not properly belong in this forum. Feel free to repost elsewhere.
-------
PrimeFaces 3.4, Glassfish 3.1.2, Mojarra 2.1.6

porthos
Posts: 122
Joined: 09 May 2011, 03:17

12 Apr 2012, 02:38

Slightly updated the original posting to remove extraneous detail in the facelet code, show the validation failure message with severity Error rather Info, and added the @FacesValidator annotation so no hidden configuration is required to make this work from a copy-paste.
-------
PrimeFaces 3.4, Glassfish 3.1.2, Mojarra 2.1.6

Post Reply

Return to “PrimeFaces”

  • Information
  • Who is online

    Users browsing this forum: No registered users and 60 guests