Primefaces 5.2 SelectManyCheckbox加载时禁用复选框异常问题
我之前在维护使用PrimeFaces 5.2 + JSF 2.2的项目时,碰到过完全一样的诡异问题:页面加载时两个复选框默认选中,其中一个通过条件禁用,结果取消选中未禁用的那个时,禁用的复选框居然也跟着"被取消"了,后端对应的setter还收到了空数组。折腾了好一阵才搞清楚原因,给你几个靠谱的解决办法:
问题根源
PrimeFaces 5.2的p:selectManyCheckbox组件在处理禁用且预选中的选项时存在逻辑bug:浏览器在表单提交时不会发送禁用元素的参数,而PrimeFaces的组件逻辑没有正确保留这些预选中的禁用项,反而会把它们从选中集合中移除,导致后端拿到的集合丢失了原本应该保留的禁用选项,极端情况就变成了空数组。
可行解决方案
1. 后端手动补全禁用选中项(最稳妥的后端方案)
在对应属性的setter方法里,或者表单提交的处理方法中,手动检查哪些选项是应该禁用且原本预选中的,把它们重新加回到选中集合里。举个例子:
private List<String> selectedItems; // 假设这个集合存的是所有需要禁用的选项值 private List<String> disabledItemValues; // 假设这个集合存的是所有预选中的选项值(包括禁用的) private List<String> preSelectedValues; public void setSelectedItems(List<String> selectedItems) { if (selectedItems == null) { selectedItems = new ArrayList<>(); } // 合并提交过来的选中项 + 原本预选中的禁用项 Set<String> mergedSelected = new HashSet<>(selectedItems); mergedSelected.addAll( disabledItemValues.stream() .filter(preSelectedValues::contains) .collect(Collectors.toList()) ); this.selectedItems = new ArrayList<>(mergedSelected); }
这样不管前端提交过来什么,后端都会确保预选中的禁用项不会丢失。
2. 前端添加隐藏input保留禁用项值(快速前端hack)
通过JavaScript给每个禁用且选中的复选框添加一个同名的隐藏input,这样浏览器提交表单时会带上这个隐藏input的值,PrimeFaces就能正确识别这些禁用项的选中状态了。代码示例:
$(document).ready(function() { // 替换成你的selectManyCheckbox组件的完整客户端ID const checkboxContainer = $('#mulitpleCheckboxTest\\:multicheckBoxT'); checkboxContainer.find('.ui-chkbox').each(function() { const $chkInput = $(this).find('input[type="checkbox"]'); // 只处理禁用且选中的复选框 if ($chkInput.is(':disabled') && $chkInput.is(':checked')) { // 创建同名的隐藏input,值和复选框一致 const $hiddenInput = $('<input>') .attr('type', 'hidden') .attr('name', $chkInput.attr('name')) .val($chkInput.val()); $(this).append($hiddenInput); } }); });
这个方法不需要改后端代码,适合快速临时修复。
3. 升级PrimeFaces版本(一劳永逸)
这个bug在PrimeFaces 6.0及以后的版本中已经被官方修复了。如果项目允许的话,升级到6.2或者更稳定的LTS版本(比如8.x、10.x)是最好的解决方式,不过升级前一定要做好兼容性测试,避免其他组件出现新问题。
额外注意:检查你的XHTML代码
确保你是正确绑定禁用条件的,比如:
<h:form id="mulitpleCheckboxTest"> <p:selectManyCheckbox id="multicheckBoxT" value="#{yourBean.selectedItems}"> <f:selectItems value="#{yourBean.availableItems}" var="item" itemLabel="#{item.displayText}" itemValue="#{item.value}" itemDisabled="#{item.isDisabled}" /> <!-- 这里正确绑定禁用条件 --> <p:column style="width:25%" /> <p:column style="width:25%" /> </p:selectManyCheckbox> </h:form>
一定要保证禁用的选项在渲染时确实带有disabled属性,不然前端的JS修复也不会生效。
内容的提问来源于stack exchange,提问作者Newbie

