Friday, February 25, 2011

Custom JSF validator for required fields

JSF components implementing EditableValueHolder interface have two attributes "required" and "requiredMessage" - a flag indicating that the user is required to input / select not empty value and a text for validation message. We can use that, but it's not flexible enough, we can't parameterize the message directly in view (facelets or jsp) and we have to do something for a proper message customization. What is about a custom validator attached to any required field? We will write one. At first we need to register such validator in a tag library.
<?xml version="1.0"?>
<facelet-taglib version="2.0" ... >
    <namespace>http://ip.client/ip-jsftoolkit/validator</namespace>
    <tag>
        <tag-name>requiredFieldValidator</tag-name>
        <validator>
            <validator-id>ip.client.jsftoolkit.RequiredFieldValidator</validator-id>
        </validator>
        <attribute>
            <description>Resource bundle name for the required message</description>
            <name>bundle</name>
            <required>false</required>
            <type>java.lang.String</type>
        </attribute>
        <attribute>
            <description>Key of the required message in the resource bundle</description>
            <name>key</name>
            <required>false</required>
            <type>java.lang.String</type>
        </attribute>
        <attribute>
            <description>Label string for the required message</description>
            <name>label</name>
            <required>false</required>
            <type>java.lang.String</type>
        </attribute>
    </tag>
</facelet-taglib>
We defined three attributes in order to achieve a high flexibility. A simple using would be
<h:outputLabel for="myInput" value="#{text['myinput']}"/>
<h:inputText id="myInput" value="...">
    <jtv:requiredFieldValidator label="#{text['myinput']}"/>
</h:inputText>
The validator class itself is not difficult. Dependent on the "key" parameter (key of the required message) and the "label" parameter (text of the corresponding label) there are four cases how the message gets acquired.
/**
 * Validator for required fields.
 */
@FacesValidator(value = RequiredFieldValidator.VALIDATOR_ID)
public class RequiredFieldValidator implements Validator
{
    /** validator id */
    public static final String VALIDATOR_ID = "ip.client.jsftoolkit.RequiredFieldValidator";

    /** default bundle name */
    public static final String DEFAULT_BUNDLE_NAME = "ip.client.jsftoolkit.validator.message";

    private String bundle;
    private String key;
    private String label;

    @Override
    public void validate(FacesContext facesContext,
        UIComponent component, Object value) throws ValidatorException
    {
        if (!UIInput.isEmpty(value)) {
           return;
        }

        String message;
        String bundleName;

        if (bundle == null) {
           bundleName = DEFAULT_BUNDLE_NAME;
        } else {
           bundleName = bundle;
        }

        if (key == null && label == null) {
            message = MessageUtils.getMessageText(
               MessageUtils.getResourceBundle(facesContext, bundleName),
                  "jsftoolkit.validator.emptyMandatoryField.1");
        } else if (key == null && label != null) {
            message = MessageUtils.getMessageText(
               MessageUtils.getResourceBundle(facesContext, bundleName),
                  "jsftoolkit.validator.emptyMandatoryField.2", label);
        } else if (key != null && label == null) {
            message = MessageUtils.getMessageText(
               MessageUtils.getResourceBundle(facesContext, bundleName), key);
        } else {
            message = MessageUtils.getMessageText(
               MessageUtils.getResourceBundle(facesContext, bundleName), key, label);
        }

        throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_WARN, message, StringUtils.EMPTY));
       
        // getter / setter
        ... 
    }
}
MessageUtils is an utility class to get ResourceBundle and message text. We also need two text in the resource bundle (property file)
jsftoolkit.validator.emptyMandatoryField.1=Some required field is not filled in.
jsftoolkit.validator.emptyMandatoryField.2=The required field '{0}' is not filled in.
and the following context parameter in web.xml
<context-param>
    <param-name>javax.faces.VALIDATE_EMPTY_FIELDS</param-name>
    <param-value>true</param-value>
</context-param>
This solution is not ideal because we need to define the label text (like #{text['myinput']}) twice and to attach the validator to each field to be validated. A better and generic validator for multiple fields will be presented in the next post. Stay tuned!

2 comments:

  1. I have no words for this great post such a awe-some information i got gathered. Thanks to Author.
    html5 converter

    ReplyDelete
  2. Thanks for every other wonderful article. Where else may anybody get that kind of information in such an ideal
    way of writing? I have a presentation subsequent week, and I am at the look for such information.

    ReplyDelete