JSF 1.2中FacesMessages出现时如何隐藏自定义必填星号组件
基于JSF 1.2开发动态生成表单:表单字段、字段类型(复选框、单选按钮等)均为动态配置,通过v.required属性可识别字段是否为必填项。
业务要求页面初始加载时就展示带“Required value”提示的*必填标识,用户无需提交即可提前知晓必填项。但站点已通过FacesMessages实现原生错误提示机制,会展示“Incorrect characters”“Required value”类校验提示,当前存在的问题是:
- 页面首次加载时,自定义
*标识展示正常 - 点击“Accept”按钮提交触发表单校验后,自定义
*标识和FacesMessages抛出的“Value required”提示同时出现,造成提示重复。
当前自定义必填标识的实现代码:
<h:outputText for="#{idUtil.For(v)}" ajaxRendered="true" value="*" styleClass="required" rendered="#{v.requerido and facesMessages.getCurrentMessagesForControl(idUtil.For(v)).size()==0}" title="Required value" />
效果对比
- 首次加载正常效果:

- 提交后重复提示效果:

- 对应字段出现FacesMessages校验提示时,如何自动隐藏自定义
*标识,避免重复 - JSF 1.2是否支持按CSS class重渲染(reRender)组件——当前所有字段为动态生成,无法提前写死待重渲染的静态组件ID。
1. 修复自定义必填标识的显示判断逻辑
你当前的rendered判断逻辑方向正确,但失效的核心原因是:站点封装的facesMessages.getCurrentMessagesForControl()方法存在渲染时序问题——该方法通常会在FacesMessages提示组件渲染完成后移除对应消息,如果你自定义的*标识渲染顺序早于消息组件,拿到的消息长度始终为0,自然无法触发隐藏。
直接替换为JSF原生API判断即可,不受渲染顺序影响:
rendered="#{v.requerido and not facesContext.getMessages(idUtil.For(v)).hasNext()}"
该逻辑直接读取JSF请求生命周期内绑定到对应控件ID的消息队列,只要队列中存在该控件的任何校验消息,就自动隐藏自定义*标识,从根源避免重复提示。
2. 动态组件重渲染实现
JSF 1.2及配套的RichFaces 3.x/MyFaces Tomahawk ajax组件原生不支持按CSS class选择器重渲染组件,仅支持按组件ID重渲染,针对动态表单场景,不需要逐个指定静态ID,二选一即可:
方案一:统一容器重渲染(实现成本最低)
将所有动态表单内容(字段、必填标识、错误提示)统一包裹在一个固定ID的容器组件中,提交时直接重渲染整个容器:
<h:panelGroup id="dynamicFormWrap"> <!-- 循环渲染所有动态字段的区域 --> <ui:repeat value="#{form.dynamicFields}" var="v"> <!-- 字段输入组件、自定义*标识、FacesMessages提示 代码 --> </ui:repeat> </h:panelGroup> <!-- 提交按钮配置,直接重渲染整个表单容器 --> <h:commandButton value="Accept" action="#{form.handleSubmit}"> <a4j:support event="onclick" ajaxSingle="false" reRender="dynamicFormWrap" /> </h:commandButton>
该方案适合单页字段数少于100的常规表单,开发成本极低,无额外逻辑。
方案二:动态收集错误组件ID重渲染(性能最优)
如果表单字段量级较大,不想整体重渲染,可在服务端校验逻辑执行完成后,动态收集所有绑定了错误消息的字段ID,拼接为JSF重渲染支持的逗号分隔ID串,放入请求作用域即可:
// 校验逻辑执行完成后调用 FacesContext ctx = FacesContext.getCurrentInstance(); List<String> errorFieldIds = new ArrayList<>(); Iterator<String> clientIdIt = ctx.getClientIdsWithMessages(); while (clientIdIt.hasNext()) { errorFieldIds.add(clientIdIt.next()); } // 将拼接好的ID串存入请求域 ctx.getExternalContext().getRequestMap().put("targetReRenderIds", String.join(",", errorFieldIds));
提交按钮的重渲染配置直接引用该变量:
<a4j:support event="onclick" ajaxSingle="false" reRender="#{targetReRenderIds}" />
该方案只会重渲染实际存在校验错误的字段,性能开销最小,适合大字段量的复杂动态表单。
内容的提问来源于stack exchange,提问作者devblock

