My JSF Books/Videos My JSF Tutorials OmniFaces/JSF PPTs
JSF 2.3 Tutorial
JSF Caching Tutorial
JSF Navigation Tutorial
JSF Scopes Tutorial
JSF Page Author Beginner's Guide
OmniFaces 2.3 Tutorial Examples
OmniFaces 2.2 Tutorial Examples
JSF Events Tutorial
OmniFaces Callbacks Usages
JSF State Tutorial
JSF and Design Patterns
JSF 2.3 New Features (2.3-m04)
Introduction to OmniFaces
25+ Reasons to use OmniFaces in JSF
OmniFaces Validators
OmniFaces Converters
JSF Design Patterns
Mastering OmniFaces
Reusable and less-verbose JSF code

My JSF Resources ...

Java EE Guardian
Member of JCG Program
Member MVB DZone
Blog curated on ZEEF
OmniFaces is an utility library for JSF, including PrimeFaces, RichFaces, ICEfaces ...

[OmniFaces Utilities] - Find the right JSF OmniFaces 2 utilities methods/functions

Search on blog

Petition by Java EE Guardians

Twitter

miercuri, 20 ianuarie 2016

Using PrimeFaces and OmniFaces to customize the PrimeFaces client-side bean validation error messages

As you probably know, PrimeFaces support Bean Validation. Actually, PrimeFaces comes with a client side validation framework integrated with Bean Validation Specification.  Basically, PrimeFaces obtains via a set of classes (located in org.primefaces.validate.bean) the server side validation constraints for each type of supported validators. These constrains will be used in a JavaScript file named beanvalidation.js to perform the validation on client side and generate the corresponding messages. For example, let's suppose that we have the following bean:

@Named
@RequestScoped
public class DataBean {

 @Size(min = 2, max = 25)
 private String name;
 @Size(min = 2, max = 10)
 private String surname;

 // getters and setters
}

PrimeFaces extracts the constraints for @Size via org.primefaces.validate.bean.SizeClientValidationConstraint, like below:

package org.primefaces.validate.bean;

import java.util.HashMap;
import java.util.Map;
import javax.validation.constraints.Size;
import javax.validation.metadata.ConstraintDescriptor;
import org.primefaces.util.HTML;

public class SizeClientValidationConstraint implements ClientValidationConstraint {

 private static final String MESSAGE_METADATA = "data-p-size-msg";
 private static final String MESSAGE_ID = "{javax.validation.constraints.Size.message}";

 public Map<String, Object> getMetadata(ConstraintDescriptor constraintDescriptor) {
       
  Map<String, Object> metadata = new HashMap<String, Object>();
  Map attrs = constraintDescriptor.getAttributes();
  Object message = attrs.get("message");

  metadata.put(HTML.VALIDATION_METADATA.MIN_LENGTH, attrs.get("min"));
  metadata.put(HTML.VALIDATION_METADATA.MAX_LENGTH, attrs.get("max"));

  if (!message.equals(MESSAGE_ID)) {
      metadata.put(MESSAGE_METADATA, message);
  }

  return metadata;
 }

 public String getValidatorId() {
  return Size.class.getSimpleName();
 }
}

Now, the extracted constrains are used in beanvalidation.js, as below:

if (window.PrimeFaces) {
    ...
    PrimeFaces.locales.en_US.messages["javax.validation.constraints.Size.message"] = "size must be between {0} and {1}";

    PrimeFaces.validator.Size = {
    MESSAGE_ID: "javax.validation.constraints.Size.message",
      validate: function(d, f) {
       if (f !== null) {
           var e = d.val().length,
           c = d.data("p-minlength"),
           a = d.data("p-maxlength"),
           b = PrimeFaces.util.ValidationContext;
           if (e < c || e > a) {
               throw b.getMessageBV(d, this.MESSAGE_ID, d.data("p-size-msg"), c, a)
           }
       }
      }
    };
    ...  
};

So, this will produce the error messages from figure below:


Now, let's suppose that we want to change this message and involve more information in it. For example, let's suppose that we want to provide in the error messages the labels of the invalid components. So, instead of the above image, we want to obtain something like this:


Note In case of server-side validation via Bean Validation, you can accomplish this task via OmniFaces JsfLabelMessageInterpolator.

In order to accomplish this task we have several approaches. One of these approaches consist in overriding the SizeClientValidationConstraint for extracting the validated input label and overriding the client-side implementation to take this label into account.

So, first we will write a new SizeClientValidationConstraint like below:

import java.util.HashMap;
import java.util.Map;
import javax.validation.constraints.Size;
import javax.validation.metadata.ConstraintDescriptor;
import static org.omnifaces.util.Components.getCurrentComponent;
import static org.omnifaces.util.Components.getLabel;
import org.primefaces.util.HTML;

public class SizeClientValidationConstraint implements ClientValidationConstraint {

 private static final String MESSAGE_METADATA = "data-p-size-msg";
 private static final String MESSAGE_ID = "{javax.validation.constraints.Size.message}";

 public Map<String, Object> getMetadata(ConstraintDescriptor constraintDescriptor) {
       
  Map<String, Object> metadata = new HashMap<String, Object>();
  Map attrs = constraintDescriptor.getAttributes();
  Object message = attrs.get("message");

  metadata.put(HTML.VALIDATION_METADATA.MIN_LENGTH, attrs.get("min"));
  metadata.put(HTML.VALIDATION_METADATA.MAX_LENGTH, attrs.get("max"));
  metadata.put(HTML.VALIDATION_METADATA.LABEL, getLabel(getCurrentComponent()));       

  if (!message.equals(MESSAGE_ID)) {
      metadata.put(MESSAGE_METADATA, message);
  }

  return metadata;
 }

 public String getValidatorId() {
  return Size.class.getSimpleName();
 }
}

Thanks to OmniFaces utilities, we can obtain the label of the current validated input component via Components#getCurrentComponent() and Components#getLabel(). This save us for writing a good chunk of code! Further, we simply add the label in the metadata under HTML.VALIDATION_METADATA.LABEL.

Now, on client-side, we adjust the JavaScript code to use this label in error validation messages for @Size:

<script type="text/javascript">
 //<![CDATA[
 PrimeFaces.locales.en_US.messages["javax.validation.constraints.Size.message"] = "The size of {0} must be between {1} and {2} characters";
 PrimeFaces.validator.Size = {
  MESSAGE_ID: "javax.validation.constraints.Size.message",
  validate: function (d, f) {
  if (f !== null) {
      var e = d.val().length,
          c = d.data("p-minlength"),
          a = d.data("p-maxlength"),
          l = d.data("p-label");
          b = PrimeFaces.util.ValidationContext;
          if (e < c || e > a) {
              throw b.getMessageBV(d, this.MESSAGE_ID, d.data("p-size-msg"), l, c, a)
          }
   }
  }
 };
 //]]>  
</script>

Done! The complete application in available here.

Read more such goodies in:

PrimeFaces & OmniFaces - Powers Combined

Niciun comentariu :

Trimiteți un comentariu

JSF BOOKS COLLECTION

Postări populare

Follow by Email

Visitors Starting 4 September 2015

Locations of Site Visitors