Группа JSF и Bean Validation
Я использую JavaEE 7 (JSF 2.2, Bean Validation 1.1...) и простые символы 5.1 в среде выполнения WildFly 8.1
Я хочу использовать разные группы проверки в соответствии с нажатой кнопкой. Кнопка сохранения, связанная с небольшим количеством ограничений, и кнопка отправки, связанная с большим количеством ограничений.
Кажется, мне нужны группы проверки компонентов, но у меня есть некоторые проблемы с веб-интерфейсом.
Я не хочу, чтобы неправильные данные отображались красным цветом в соответствии с режимом проверки: сохранить или отправить
Итак, я сделал 3 интерфейса:
public interface LifeCycleValidation {}
public interface Save extends LifeCycleValidation {}
public interface Submit extends Save {}
Я аннотирую поля с помощью BeanVal с соответствующей группой в объекте модели
@Size(min=3, max = 300, groups = Save.class)
@NotNull(groups = Save.class)
private String title ;
@Size(min = 3, max = 5, groups = Submit.class)
private List<String> keywords ;
Я сделал несколько методов в управляемом бине JSF
private final String formIdPrefix = "bookEditForm:" ;
public void save() {
save(Save.class) ;
}
public void submit() {
save(Submit.class) ;
}
private void save(Class<? extends LifeCycleValidation> groupClass) {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory() ;
Validator validator = factory.getValidator();
Set<ConstraintViolation<Book>> violations = validator.validate(this, groupClass);
violations.stream()
.forEach(violation -> addErrorMessage(formIdPrefix+violation.getPropertyPath().toString(), violation.getMessage()));
if(violations.isEmpty()) {
// save
addInfoMessage("","sucess") ;
} else {
addErrorMessage("",violations.size()+" error(s)") ;
}
}
private void addErrorMessage(String id, String msg) {
addMessage(id,FacesMessage.SEVERITY_ERROR,"Error",msg) ;
}
private void addInfoMessage(String id, String msg) {
addMessage(id,FacesMessage.SEVERITY_INFO,"Info",msg) ;
}
private void addMessage(String clientId, FacesMessage.Severity severity, String summary, String detail) {
FacesMessage message = new FacesMessage(severity, summary, detail);
FacesContext.getCurrentInstance().addMessage(clientId, message);
}
Сообщения отображаются правильно, если идентификатор входа соответствует пути свойства, но вход не красный
<h:form id="bookEditForm">
<p:messages showDetail="true" showSummary="true"/>
<p:panelGrid columns="3">
<p:outputLabel value="title" for="title"/>
<p:inputText value="#{book.title}" id="title"/>
<p:message showDetail="true" showSummary="true" for="title"/>
<p:outputLabel value="keywords" for="keywords"/>
<p:inputText value="#{book.keywords}" id="keywords" converter="converter.ListString"/>
<p:message showDetail="true" showSummary="true" for="keywords"/>
</p:panelGrid>
<p:commandButton value="save" action="#{book.save()}" update="@form"/>
<p:commandButton value="submit" action="#{book.submit()}" update="@form"/>
</h:form>
(в моем примере модель и контроллер находятся в одном классе, это был просто тест)
Я пробую другое решение, используя f:validateBean validationGroups, поэтому я сделал перечисление ValidationMode:
public enum ValidationMode {
SAVE(Save.class), SUBMIT(Submit.class);
public final Class<? extends LifeCycleValidation> cl ;
private ValidationMode(Class<? extends LifeCycleValidation> cl) {
this.cl = cl ;
}
}
и поместите его в управляемый компонент JSF:
private ValidationMode validationMode ;
public void setValidationMode(String validationModeTitle) {
this.validationMode = ValidationMode.valueOf(validationModeTitle);
}
public String getValidationGroups() {
if(validationMode==null) {
return ValidationMode.SAVE.cl.getCanonicalName() ;
}
return validationMode.cl.getCanonicalName() ;
}
и попробуйте установить соответствующий режим в actionListener их commandButton
<h:form id="bookEditForm">
<p:messages showDetail="true" showSummary="true"/>
<p:panelGrid columns="3">
<p:outputLabel value="title" for="title"/>
<p:inputText value="#{book.title}" id="title">
<f:validateBean validationGroups="#{book.validationGroups}"/>
</p:inputText>
<p:message showDetail="true" showSummary="true" for="title"/>
<p:outputLabel value="keywords" for="keywords"/>
<p:inputText value="#{book.keywords}" id="keywords" converter="converter.ListString">
<f:validateBean validationGroups="#{book.validationGroups}"/>
</p:inputText>
<p:message showDetail="true" showSummary="true" for="keywords"/>
</p:panelGrid>
<p:commandButton value="save" action="#{book.save()}" actionListener="#{book.setValidationMode('SAVE')}" update="@form"/>
<p:commandButton value="submit" action="#{book.submit()}" actionListener="#{book.setValidationMode('SUBMIT')}" update="@form"/>
</h:form>
но это не работает
Я думаю написать свой собственный валидатор jsf, но я не знаю, как реализовать валидатор для проверки поля в соответствии с правильной группой
1 ответ
В конце концов я нашел что-то, и я запускаю setValidationMode, когда происходит случайное переключение, как будто режим проверки установлен перед тем, как вызывается действие: сохранить или отправить
<h:panelGroup layout="span">
<p:ajax event="mouseover" listener="#{book.setValidationMode('SAVE')}"/>
<p:commandButton value="save" action="#{book.save()}" update="@form"/>
</h:panelGroup>
<h:panelGroup layout="span">
<p:ajax event="mouseover" listener="#{book.setValidationMode('SUBMIT')}"/>
<p:commandButton value="submit" action="#{book.submit()}" update="@form"/>
</h:panelGroup>
так что вам нечего делать в контроллере
private ValidationMode validationMode = ValidationMode.SAVE ;
public void setValidationMode(String validationModeTitle) {
this.validationMode = ValidationMode.valueOf(validationModeTitle);
}
public String getValidationGroups() {
return validationMode.cl.getCanonicalName() ;
}
public void save() {
// save
addInfoMessage("","saved") ;
}
public void submit() {
// submit
addInfoMessage("","submited") ;
}
редактировать
Вот чистое и полное решение MVC с прогрессбаром, рассчитанным в бонусе:
вид:
<h:form id="bookEditForm">
<p:progressBar value="#{bookBean.progress}" labelTemplate="{value}%" id="progress"/>
<p:messages showDetail="true" showSummary="true"/>
<p:panelGrid columns="3">
<p:outputLabel value="title" for="title"/>
<p:inputText value="#{bookBean.book.title}" id="title">
<f:validateBean validationGroups="#{bookBean.validationGroups}"/>
</p:inputText>
<p:message showDetail="true" showSummary="true" for="title"/>
<p:outputLabel value="keywords" for="keywords"/>
<p:inputText value="#{bookBean.book.keywords}" id="keywords" converter="converter.ListString">
<f:validateBean validationGroups="#{bookBean.validationGroups}"/>
</p:inputText>
<p:message showDetail="true" showSummary="true" for="keywords"/>
</p:panelGrid>
<h:panelGroup layout="span">
<p:ajax event="mouseover" listener="#{bookBean.setValidationMode('SAVE')}"/>
<p:commandButton value="save" action="#{bookBean.save()}" update="@form"/>
</h:panelGroup>
<h:panelGroup layout="span">
<p:ajax event="mouseover" listener="#{bookBean.setValidationMode('SUBMIT')}"/>
<p:commandButton value="submit" action="#{bookBean.submit()}" update="@form"/>
</h:panelGroup>
</h:form>
контроллер:
@Named
@ViewScoped
public class BookBean implements Serializable {
private ValidationMode validationMode = ValidationMode.SAVE;
private Book book ;
public BookView() {
book = new Book() ;
}
public void setValidationMode(String validationModeTitle) {
this.validationMode = ValidationMode.valueOf(validationModeTitle);
}
public String getValidationGroups() {
return validationMode.cl.getCanonicalName() ;
}
public Book getBook() {
return book;
}
public void save() {
// save...
addMessage("",FacesMessage.SEVERITY_INFO,"Success","Infos saved") ;
}
public void submit() {
//submit...
addMessage("",FacesMessage.SEVERITY_INFO,"Success","Infos submitted") ;
}
private void addMessage(String clientId, FacesMessage.Severity severity, String summary, String detail) {
FacesMessage message = new FacesMessage(severity, summary, detail);
FacesContext.getCurrentInstance().addMessage(clientId, message);
}
public int getProgress() {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory() ;
Validator validator = factory.getValidator();
int nbConstraints = validator.getConstraintsForClass(Book.class).getConstrainedProperties().size() ;
int nbViolations = validator.validate(book, Submit.class).size();
return 100 - (nbViolations*100) / (nbConstraints) ;
}
}
модель:
public class Book implements Serializable {
@NotNull(groups = Save.class)
@Size(min=1, max = 140, groups = Save.class)
private String title ;
@NotNull(groups = Submit.class)
@Size(min = 3, max = 5, groups = Submit.class)
private List<String> keywords ;
// getters and setters
}
ValidationMode, LifeCycleValidation, Save и Submit такие же, как в вопросе
И у OmniFaces, похоже, есть хорошее решение: http://showcase.omnifaces.org/validators/validateBean Может быть интересно...