Extending CSV with JSF

The Client Side Validation API makes it possible to write our own validators and converters. The writing process is straightforward.

In this recipe, we will develop a custom JSF validator that validates any Unicode strings. The validator will check whether an input value consists of letters, spaces, hyphens, and apostrophes. Any other characters are not allowed. This is a common requirement for validating names of persons, addresses, and similar inputs. We will use the CSV API to implement both server-side and client-side validators. After that, we will see validation in action.

How to do it…

A custom JSF validator must implement two interfaces: javax.faces.validator.Validator and org.primefaces.validate.ClientValidator. The first interface defines a well-known validate() method, and the second one defines the getMetadata() and getValidatorId() methods. Implementing the getMetadata() method should provide optional metadata. Implementing the getValidatorId() method should provide a unique ID. Both pieces of information are used in the client-side validator implemented in JavaScript. As metadata, we would like to use a custom parameter for the fixed localized message. The parameter will, therefore, be exposed to the JavaScript code. We will start with the server-side implementation. The complete validator code looks like this:

@FacesValidator("org.primefaces.cookbook.UnicodeValidator")
public class UnicodeValidator
  implements Validator, ClientValidator, Serializable {

  private static final String MESSAGE_METADATA = "data-param";
  private static final String REGEX =
    "[\p{L}\-'\´\`\s]+";

  private String msgparam;

  @Override
  public void validate(FacesContext context,
    UIComponent component,
    Object value) throws ValidatorException {
    if (value == null) {
      return;
    }

    boolean valid = value.toString().matches(REGEX);
    if (!valid) {
      String param = MessageUtil.getMessage(msgparam);
      String msg = MessageUtil.getMessage(
        "invalid.unicode", param);
      throw new ValidatorException(new FacesMessage(
        FacesMessage.SEVERITY_ERROR, null, msg));
    }
  }

  @Override
  public Map<String, Object> getMetadata() {
    Map<String, Object> metadata =
      new HashMap<String, Object>();
    String param = MessageUtil.getMessage(msgparam);
    metadata.put(MESSAGE_METADATA, param);

    return metadata;
  }

  @Override
  public String getValidatorId() {
    return UnicodeValidator.class.getSimpleName();
  }

  public String getMsgparam() {
    return msgparam;
  }

  public void setMsgparam(String msgparam) {
    this.msgparam = msgparam;
  }
}

We use the [\p{L}\-'\´\`\s]+ regular expression to validate user input. MessageUtil is a utility class used to get message text from resource bundles. It is not specified here. The msgparam property is mentioned in the preceding variable parameter for the localized message. The validator should be registered in facelet-taglib:

<?xml version="1.0"?>
<facelet-taglib version="2.2" ...>
  <namespace>
    http://primefaces.org/ui/cookbook
  </namespace>

  <tag>
    <tag-name>validateUnicode</tag-name>
    <validator>
      <validator-id>
        org.primefaces.cookbook.UnicodeValidator
      </validator-id>
    </validator>
    <attribute>
      <name>msgparam</name>
      <required>true</required>
      <type>java.lang.String</type>
    </attribute>
  </tag>
</facelet-taglib>

The message keys and text for this example are defined in property files acting as resource bundles. The English version looks like this:

firstName=First Name
lastName=Last Name
invalid.unicode={0} may only contain letters, spaces, hyphens and apostrophes

Let's go to the client-side implementation. First, we have to create a JavaScript file, say validators.js, and register there our own validator in the PrimeFaces.validator namespace with the name UnicodeValidator. This name is the unique ID mentioned earlier. The function to be implemented is called validate(). It has two parameters—the element itself and the current input value to be validated. The following code shows this:

PrimeFaces.validator['UnicodeValidator'] = {
  regex: XRegExp("^[\p{L}-'´`\s]+$"),

  MESSAGE_ID: 'invalid.unicode',

  validate: function (element, value) {
    if (!this.regex.test(value)) {
      throw PrimeFaces.util.ValidationContext.getMessage(
        this.MESSAGE_ID, element.data('param'));
    }
  }
};

Second, we have to create a JavaScript file for localized messages, for example, lang_en.js. The messages should be the same as already defined in the server-side resource bundles. The following code shows this:

PrimeFaces.locales['en'] = {
  messages : PrimeFaces.locales['en_US'].messages
};

$.extend(PrimeFaces.locales['en'].messages, {
  ...

  'invalid.unicode':
  '{0} may only contain letters, spaces, hyphens and apostrophes'
});

The bean contains two properties of the String type. The following code shows this:

@Named
@ViewScoped
public class ExtendCsvBean implements Serializable {

  private String firstName;
  private String lastName;

  // getters / setters
  ...
}

Now, we can take the xmlns:book="http://primefaces.org/ui/cookbook" namespace from facelet-taglib and write the following XHTML snippet:

<h:panelGrid columns="3" cellpadding="3"
  style="margin-bottom:10px;">
  <p:outputLabel for="firstName" value="First Name"/>
  <p:inputText id="firstName"
    value="#{extendCsvBean.firstName}">
    <book:validateUnicode msgparam="firstName"/>
  </p:inputText>
  <p:message for="firstName"/>

  <p:outputLabel for="lastName" value="Last Name"/>
  <p:inputText id="lastName"
    value="#{extendCsvBean.lastName}">
    <book:validateUnicode msgparam="lastName"/>
  </p:inputText>
  <p:message for="lastName"/>
</h:panelGrid>

<p:commandButton validateClient="true"
  value="Submit" ajax="false"/>

The book:validateUnicode validator is attached to p:inputText:

Tip

Using <f:validator validatorId=" org.primefaces.cookbook.UnicodeValidator"/> could be possible too if we were not using any attributes for the validator tag.

In the last step, all required JavaScript files have to be included on the page. Besides lang_en.js and validators.js, we need to include a JavaScript library for extensible regular expressions supporting Unicode and more (http://xregexp.com). The following code shows this:

<h:outputScript library="js" name="chapter10/lang_en.js"/>
<h:outputScript library="js" name="chapter10/xregexp-all.js"/>
<h:outputScript library="js" name="chapter10/validators.js"/>

Validation happens on the client-side without a round trip to the server. Setting validateClient="false" would trigger a server-side validation. The following screenshot shows the end result when validation fails:

How to do it…

How it works…

The getMetadata()method provides a map with name-value pairs. The metadata is exposed in the rendered HTML. The values can be accessed on the client side via element.data(name), where element is a jQuery object for the underlying native HTML element. The metadata in the example is rendered as follows:

<input data-param="First Name" .../>

<input data-param="Second Name" .../>

Client-side validation happens by means of the useful JavaScript library, XRegExp. We need this library because the native JavaScript regular expressions do not support Unicode (the \p{L} expression in the regex). If validation fails, we throw an exception by invoking throw PrimeFaces.util.ValidationContext.getMessage(text, parameter).

There's more…

Client-side converters can be implemented similarly. On the server side, PrimeFaces provides the org.primefaces.convert.ClientConverter interface with the getMetadata() and getConverterId() methods. The meaning of these methods is the same as for validators. On the client side, you need to override the convert: function(element, submittedValue){} function returning a converted JavaScript object. All client-side converters are defined in the PrimeFaces.converter namespace.

Tip

Refer to the PrimeFaces sources and explore the standard converters in the validation.js file to understand the writing of custom converters.

See also

Extending CSV with Bean Validation is the topic of the next recipe, Extending CSV with Bean Validation.

PrimeFaces Cookbook Showcase application

This recipe is available in the demo web application on GitHub (https://github.com/ova2/primefaces-cookbook/tree/second-edition). Clone the project if you have not done it yet, explore the project structure, and build and deploy the WAR file on application servers compatible with Servlet 3.x, such as JBoss WildFly and Apache TomEE.

The showcase for the recipe is available at http://localhost:8080/pf-cookbook/views/chapter10/extendJsfCsv.jsf.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.151.32