我正在使用JSF 2.1 Primefaces 5.1开发一个web应用程序 . 我有一个带行版的数据表,每行有2 inputText ,2 selectOneMenu 和1 selectBooleanCheckbox . 我想在单击检查图标时执行一些业务逻辑验证:它将验证2 selectOneMenu 的值 . 为此,我创建了一个与第二个 selectOneMenu 关联的自定义验证器,用于验证这两个值:

<p:dataTable id="users_table_id" var="user" value="#{usersBean.users}" rowKey="#{user.id}" paginator="true"
            paginatorTemplate="{CurrentPageReport}  {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"
               rowsPerPageTemplate="5,10,15" emptyMessage="#{msgs.no_records}" sortBy="#{user.id}" sortOrder="ascending" rows="15" editable="true">
            <f:facet name="header">
                #{msgs.users}               
            </f:facet>
            <p:ajax event="rowEdit" listener="#{usersBean.onRowEdit}" update=":users_form_id:growl"/>
            <p:ajax event="rowEditInit" listener="#{usersBean.onRowEditInit}" update=":users_form_id:growl"/>
            <p:ajax event="rowEditCancel" listener="#{usersBean.onRowEditCancel}" update=":users_form_id:growl"/>
            <p:column headerText="#{msgs.id}" sortBy="#{user.id}" styleClass="centered-column">
                #{user.id}
            </p:column>          
            <p:column headerText="#{msgs.description}" sortBy="#{user.description}" styleClass="centered-column">
                <p:cellEditor>
                    <f:facet name="output">#{user.description}</f:facet>
                    <f:facet name="input"><p:inputText value="#{user.description}" styleClass="editable-cell"/></f:facet>
                </p:cellEditor>
            </p:column>          
            <p:column headerText="#{msgs.password}" sortBy="#{user.password}" styleClass="centered-column">
                <p:cellEditor>
                    <f:facet name="output">#{user.password}</f:facet>
                    <f:facet name="input"><p:inputText value="#{user.password}" styleClass="editable-cell"/></f:facet>
                </p:cellEditor>
            </p:column>
            <p:column headerText="#{msgs.sending_system}" sortBy="#{user.playerIn.description}" styleClass="centered-column">
                <p:cellEditor>
                    <f:facet name="output">#{user.playerIn.description}</f:facet>
                    <f:facet name="input">
                        <p:selectOneMenu binding="#{userPlayerInComponent}" styleClass="editable-cell" id="users_table_sending_system_id" value="#{user.playerIn}" effectSpeed="fast" filter="true" filterMatchMode="contains" converter="#{playerConverter}">
                            <f:selectItem itemLabel="#{msgs.select_option}" itemValue="#{null}" noSelectionOption="true"/>
                            <f:selectItems value="#{usersBean.players}" var="player" itemValue="#{player}" itemLabel="#{player.description}"/>
                        </p:selectOneMenu>
                    </f:facet>
                </p:cellEditor>
            </p:column> 
            <p:column headerText="#{msgs.receiving_system}" sortBy="#{user.playerOut.description}" styleClass="centered-column">
                <p:cellEditor>
                    <f:facet name="output">#{user.playerOut.description}</f:facet>
                    <f:facet name="input">
                        <p:selectOneMenu styleClass="editable-cell" id="users_table_receiving_system_id" value="#{user.playerOut}" effectSpeed="fast" filter="true" filterMatchMode="contains" converter="#{playerConverter}">
                            <f:selectItem itemLabel="#{msgs.select_option}" itemValue="#{null}" noSelectionOption="true"/>
                            <f:selectItems value="#{usersBean.players}" var="player" itemValue="#{player}" itemLabel="#{player.description}"/>

                            <f:validator validatorId="userValidator"/>
                            <f:attribute name="userPlayerInComponent" value="#{userPlayerInComponent}"/>
                        </p:selectOneMenu>
                    </f:facet>
                </p:cellEditor>
            </p:column>                                     
            <p:column headerText="#{msgs.active}" sortBy="#{user.active}" styleClass="centered-column">
                <p:cellEditor>
                    <f:facet name="output"><p:selectBooleanCheckbox value="#{user.active}" disabled="true"/></f:facet>              
                    <f:facet name="input"><p:selectBooleanCheckbox value="#{user.active}"/></f:facet>
                </p:cellEditor>
            </p:column>
            <p:column styleClass="datatable-row-editor">
                <p:rowEditor/>
            </p:column>
            <f:facet name="footer">
            </f:facet>      
        </p:dataTable>
@FacesValidator("userValidator")
public class UserValidator implements Validator {

    private static final Logger logger =  LogManager.getLogger(UserValidator.class);

    @Override
    public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
        logger.entry(context, component, value);

        if (value == null) {
            return; // Let required="true" handle.
        }

        UIInput userPlayerInComponent = (UIInput)component.getAttributes().get("userPlayerInComponent");
        if (!userPlayerInComponent.isValid()) {
            return; // Already invalidated. Don't care about it then.
        }

        DtoPlayer playerIn = (DtoPlayer)userPlayerInComponent.getValue();
        if (playerIn == null) {
            return; // Let required="true" handle.
        }

        DtoPlayer playerOut = (DtoPlayer)value;

        UsersBean usersBean = context.getApplication().evaluateExpressionGet(context, "#{usersBean}", UsersBean.class);

        BigDecimal userId = usersBean.getUserEditingId();
        logger.info(userId + " - " + playerIn.getDescription() + " - " + playerOut.getDescription());

        for (DtoUser dtoUser : usersBean.getUsers()) {
            if (!userId.equals(dtoUser.getId()) && dtoUser.getPlayerIn().equals(playerIn) && dtoUser.getPlayerOut().equals(playerOut)) {
                logger.info("Invalidating...");
                userPlayerInComponent.setValid(false);              
                FacesContext.getCurrentInstance().validationFailed();
                throw new ValidatorException(new FacesMessage("BOOM!"));
            }
        }
        logger.exit();
    }
}

我期待,当验证失败(并抛出 ValidatorException )时,数据表行将保持编辑模式,并且两个 selectOneMenu 用红色突出显示 . 但是会发生的是该行退出编辑模式,保留旧值 . 如果我再次编辑它,它会显示提交的值,无效的单元格会用红色标记 . 只要我尝试提交无效值,此行为就会继续 . 如果我关闭编辑模式,则会丢弃无效值,并且该行仅保留旧值(无论是在查看还是编辑模式下) .

我怎么解决这个问题?从Primefaces' showcase可以看出,当您尝试提交时,比如说,年份单元格中包含alfabetic字符的字符串,该行保持编辑模式并突出显示 . 我想达到同样的行为 .