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