Primefaces使用f:selectItems导致p:commandButton动作不触发如何解决
问题诱因
- 隐性验证失败未暴露:当
<p:selectOneMenu>配置<f:selectItems>时,JSF验证阶段会额外校验提交的转换后的值是否存在于可选列表中,这个判断依赖Language实体类的equals()方法。如果你的Language类没有基于主键重写equals()和hashCode(),即使转换器返回了正确的对象,JSF也会判定值不合法,直接终止后续流程,跳过业务方法调用。因为你没有在页面配置消息展示组件,这个验证错误不会显示出来,只会表现为点击按钮没反应、弹窗直接关闭。移除<f:selectItems>后该校验逻辑不会触发,所以功能正常。 - 按钮关闭逻辑错误:你当前把弹窗隐藏逻辑写在
onclick属性里,会在点击按钮的第一时间同步执行,不管后续AJAX请求、JSF生命周期执行是否成功,所以哪怕流程中断弹窗也会直接关闭。 - 版本bug:Primefaces 8.0RC3是候选测试版本,存在select组件和自定义转换器配合时的已知验证逻辑bug,部分场景会误判值非法,中断流程。
修复方案
必做调整
- 修正按钮弹窗关闭逻辑,调整为请求完成且无校验错误时再关闭,同时新增更新域和错误判断:
<p:commandButton action="#{bookController.addBook}" value="Submit" update="@form msg" oncomplete="if (!args.validationFailed) PF('dlg').hide();"/>
- 在弹窗内新增消息展示组件,暴露隐性错误:
<p:messages id="msg" autoUpdate="true" closable="true" />
- 检查并重写
Language实体类的equals()和hashCode()方法,示例如下:
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Language language = (Language) o; return languageId != null && languageId.equals(language.languageId); } @Override public int hashCode() { return getClass().hashCode(); }
可选兼容方案(绕过转换器/验证坑)
如果调整后仍有问题,或者不想处理实体类重写、转换器的相关问题,可以改用基础类型作为提交值,完全规避上述问题:
- 在
BookController中新增Long selectedLanguageId属性,加getter/setter - 调整select组件代码,移除转换器,直接绑定ID:
<p:selectOneMenu value="#{bookController.selectedLanguageId}" > <f:selectItem itemLabel="Choose one" itemValue="#{null}"/> <f:selectItems value="#{bookController.allLanguages}" var="language" itemLabel="#{language.languageName}" itemValue="#{language.languageId}"/> </p:selectOneMenu>
- 在
addBook方法中,手动根据selectedLanguageId查询对应的Language对象赋值即可。
版本修复
如果确认是Primefaces 8.0RC3的版本bug,直接升级到Primefaces 8.0正式版或更高版本即可解决。
内容的提问来源于stack exchange,提问作者Kokusz19
相关产品推荐
相关产品推荐

