Группа 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 Может быть интересно...

Другие вопросы по тегам