Некоторые из вас уже знают, что я и мой соавтор Мерт Чалышкан работаем над 2. выпуском « Поваренной книги PrimeFaces» . Издание Packt Publishing позволило мне опубликовать небольшую выдержку из одного рецепта новой главы «Проверка на стороне клиента». Это помогло бы информировать читателей о содержании книги. В этой записи блога я хотел бы обсудить расширение клиентской проверки PrimeFaces (CSV) с проверкой bean-компонентов.
Bean Validation — это модель проверки, доступная как часть платформы Java EE 6, которая позволяет выполнять проверку с помощью ограничений в форме аннотаций, размещенных в поле, методе или классе. JSF 2.2 поддерживает валидацию полей (свойств и их методов получения / установки) в управляемых bean-компонентах, а также в bean-компонентах Spring или CDI. Проверка на уровне класса еще не поддерживается, поскольку вы не используете такие утилиты, как OmniFaces .
CSV-версия PrimeFaces имеет встроенную интеграцию с Bean Validation. Ограничения, определенные с помощью аннотаций, могут быть проверены на стороне клиента средой CSV. Хотя API Bean Validation определяет целый набор стандартных аннотаций ограничений, можно легко представить себе ситуации, в которых этих стандартных аннотаций будет недостаточно. В этих случаях вы можете создавать собственные ограничения для конкретных требований проверки. API проверки на стороне клиента в PrimeFaces без проблем работает с пользовательскими ограничениями.
 В этом рецепте мы разработаем специальные пользовательские ограничения и валидаторы для проверки кода подтверждения карты ( CVC ).  CVC используется как функция безопасности с номером банковской карты.  Это число длиной от трех до четырех цифр.  Например, MasterCard или Visa требуют трех цифр, а American Express — четырехзначных.  Поэтому проверка CVC будет зависеть от выбранной банковской карты.  Пользователь может выбрать банковскую карту с помощью p:selectOneMenu , ввести CVC в p:inputText и после этого отправить входные данные. 
Как это сделать…
Мы начнем с пользовательской аннотации, используемой для поля CVC.
| 
 01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
 | 
import org.primefaces.validate.bean.ClientConstraint;import javax.validation.Constraint;import javax.validation.Payload;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;import static java.lang.annotation.ElementType.FIELD;import static java.lang.annotation.ElementType.METHOD;@Constraint(validatedBy = CvcConstraintValidator.class)@ClientConstraint(resolvedBy = CvcClientConstraint.class)@Target({FIELD, METHOD})@Retention(RetentionPolicy.RUNTIME)public @interface ValidCVC {    String message() default "{invalid.cvc.message}";    Class<?>[] groups() default {};    Class<? extends Payload>[] payload() default {};         // identifier of the select menu with cards    String forCardMenu() default "";} | 
  @Constraint — это обычная аннотация из API-интерфейса проверки @ClientConstraint а @ClientConstraint — из PrimeFaces CSV Framework, которая помогает разрешать метаданные.  Разработанная аннотация определяет ключ сообщения invalid.cvc.message и имеет настраиваемое свойство для forCardMenu .  Значением этого свойства является любое поисковое выражение в терминах PrimeFaces Selectors (PFS) для ссылки на меню выбора с банковскими картами.  Это необходимо, поскольку действительное значение CVC зависит от выбранной карты. 
  Целью CvcConstraintValidator является проверка длины ввода. 
| 
 01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
12 
13 
14 
15 
16 
 | 
public class CvcConstraintValidator implements ConstraintValidator<ValidCVC, Integer> {    @Override    public void initialize(ValidCVC validCVC) {    }    @Override    public boolean isValid(Integer cvc, ConstraintValidatorContext context) {        if (cvc == null || cvc < 0) {            return false;        }        int length = (int) (Math.log10(cvc) + 1);        return (length >= 3 && length <= 4);    }} | 
  Целью CvcClientConstraint является подготовка метаданных. 
| 
 01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
 | 
public class CvcClientConstraint implements ClientValidationConstraint {    private static final String CARDMENU_METADATA = "data-forcardmenu";    @Override    public Map<String, Object> getMetadata(ConstraintDescriptor constraintDescriptor) {        Map<String, Object> metadata = new HashMap<String, Object>();        Map attrs = constraintDescriptor.getAttributes();        String forCardMenu = (String) attrs.get("forCardMenu");        if (StringUtils.isNotBlank(forCardMenu)) {            metadata.put(CARDMENU_METADATA, forCardMenu);        }        return metadata;    }    @Override    public String getValidatorId() {        return ValidCVC.class.getSimpleName();    }} | 
  Давайте перейдем к реализации на стороне клиента.  Сначала нам нужно создать файл JavaScript, скажем validators.js , и зарегистрировать там наш собственный валидатор в пространстве имен PrimeFaces.validator с именем ValidCVC .  Это имя является уникальным идентификатором, возвращаемым методом getValidatorId() (см. Класс CvcClientConstraint ).  Функция для реализации называется validate() .  Он имеет два параметра: сам элемент и текущее входное значение для проверки. 
| 
 01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
 | 
PrimeFaces.validator['ValidCVC'] = {    MESSAGE_ID: 'invalid.cvc',    validate: function (element, value) {        // find out selected menu value        var forCardMenu = element.data('forcardmenu');        var selOption = forCardMenu ?            PrimeFaces.expressions.SearchExpressionFacade.                resolveComponentsAsSelector(forCardMenu).find("select").val() : null;        var valid = false;        if (selOption && selOption === 'MCD') {            // MasterCard            valid = value > 0 && value.toString().length == 3;        } else if (selOption && selOption === 'AMEX') {            // American Express            valid = value > 0 && value.toString().length == 4;        }        if (!valid) {            throw PrimeFaces.util.ValidationContext.                getMessage(this.MESSAGE_ID);        }    }}; | 
  Во-вторых, мы должны создать файл JavaScript для локализованных сообщений, например, lang_en.js . 
| 
 01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
 | 
PrimeFaces.locales['en'] = {    messages : PrimeFaces.locales['en_US'].messages};$.extend(PrimeFaces.locales['en'].messages, {    ...      'invalid.cvc':        'Card Validation Code is invalid'}); | 
  @NotNull имеет два обязательных свойства, аннотированных @NotNull .  Кроме того, свойство cvc нашей пользовательской аннотацией @ValidCVC .  Значение атрибута forCardMenu указывает на класс стиля p:selectOneMenu котором перечислены доступные банковские карты. 
| 
 01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
12 
13 
14 
15 
16 
17 
 | 
@Named@ViewScopedpublic class ExtendCsvBean implements Serializable {    @NotNull    private String card;    @NotNull    @ValidCVC(forCardMenu = "@(.card)")    private Integer cvc;    public void save() {        RequestContext.getCurrentInstance().execute("alert('Saved!')");    }      // getters / setters    ...} | 
  Во фрагменте XHTML у нас есть меню выбора с двумя банковскими картами и полем ввода для CVC.  p:commandButton проверяет поля и выполняет метод save() при обратной передаче. 
| 
 01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
 | 
<h:panelGrid id="pgrid" columns="3" cellpadding="3" style="margin-bottom:10px;">    <p:outputLabel for="card" value="Card"/>    <p:selectOneMenu id="card" styleClass="card"                     value="#{extendCsvBean.card}">        <f:selectItem itemLabel="Please select a card"                      itemValue="#{null}"/>        <f:selectItem itemLabel="MasterCard"                      itemValue="MCD"/>        <f:selectItem itemLabel="American Express"                      itemValue="AMEX"/>    </p:selectOneMenu>    <p:message for="card"/>    <p:outputLabel for="cvc" value="CVC"/>    <p:inputText id="cvc" value="#{extendCsvBean.cvc}"/>    <p:message for="cvc"/></h:panelGrid><p:commandButton validateClient="true" value="Save"                 process="@this pgrid" update="pgrid" action="#{extendCsvBean.save}"/> | 
  Примечание. Как видите, ни p:selectOneMenu ни p:inputText определяют обязательный атрибут.  Мы можем добиться преобразования аннотации @NotNull в обязательный атрибут со значением true если для параметра контекста primefaces.TRANSFORM_METADATA значение true . 
На последнем шаге все необходимые файлы JavaScript должны быть включены на страницу.
| 
 1 
2 
 | 
<h:outputScript library="js" name="chapter10/lang_en.js"/><h:outputScript library="js" name="chapter10/validators.js"/> | 
На следующих двух рисунках показано, что происходит при сбое проверки
Если все в порядке, появится окно с текстом «Сохранено»! отображается для пользователя.
Как это работает…
  Ключ сообщения invalid.cvc.message и текст должны быть помещены в пакеты ресурсов с именем ValidationMessages , например, ValidationMessages_en.properties .  ValidationMessages — это стандартное имя, указанное в спецификации Bean Validation.  Файлы свойств должны находиться в пути к классам приложения и содержать следующую запись: invalid.cvc.message=Card Validation Code is invalid .  Эта конфигурация важна для проверки на стороне сервера. 
  Метод getMetadata() в классе CvcClientConstraint предоставляет карту с именем, значением пары.  Метаданные представлены в отображаемом HTML.  Доступ к значениям на стороне клиента element.data(name) через element.data(name) , где element — это объект jQuery для базового нативного HTML-элемента.  Поле CVC с метаданными отображается как 
| 
 1 
2 
 | 
<input type="text" data-forcardmenu="@(.card)"       data-p-con="javax.faces.Integer" data-p-required="true"...> | 
  Наиболее интересной частью является реализация валидатора на стороне клиента.  Проверяемое значение уже числовое, потому что сначала оно конвертируется встроенным в клиентский конвертер PrimeFaces для типа данных java.lang.Integer .  Нам нужно только проверить, является ли значение положительным и имеет допустимую длину.  Допустимая длина зависит от выбранной карты в меню p:selectOneMenu которой может обращаться JavaScript API PrimeFaces.expressions.SearchExpressionFacade.resolveComponentsAsSelector(selector) как PrimeFaces.expressions.SearchExpressionFacade.resolveComponentsAsSelector(selector) , где селектор — любой селектор PrimeFaces, в нашем случае @(.card) ,  Если проверка завершается неудачно, мы генерируем исключение, вызывая throw PrimeFaces.util.ValidationContext.getMessage(text, parameter) . 
  Проверка на стороне клиента запускается установкой validateClient=”true” в p:commandButton . 
| Ссылка: | Расширение PrimeFaces CSV с помощью Bean Validation от нашего партнера по JCG Олега Вараксина в блоге Мысли о разработке программного обеспечения . | 


