Некоторые из вас уже знают, что я и мой соавтор Мерт Чалышкан работаем над 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 Олега Вараксина в блоге Мысли о разработке программного обеспечения . |


