JSF 2.2自定义组件如何将内部验证消息传递至关联组件消息?
解决JSF自定义组件内部验证消息关联到组件消息的问题
要让自定义组件内部<p:inputText>的验证消息显示在指向自定义组件id的<p:message>中,核心是把内部输入组件的验证消息转移到自定义组件本身——因为默认情况下,验证消息会绑定到实际执行验证的UIInput组件(也就是内部的<p:inputText>)。下面是具体实现步骤:
步骤1:让自定义组件成为命名容器
首先修改自定义组件的<cc:interface>,指定一个自定义组件类型,让它实现NamingContainer接口,这能确保自定义组件的id可以作为消息关联的有效容器。
修改后的Facelet代码:
<cc:interface componentType="com.example.MyCustomComponent"> <cc:attribute name="id" required="true"/> <cc:attribute name="value" required="true"/> </cc:interface> <cc:implementation> <!-- 绑定内部input到自定义组件的属性,方便后续处理 --> <p:inputText binding="#{cc.innerInput}" value="#{cc.attrs.value}" required="true"/> </cc:implementation>
步骤2:编写自定义组件的Java类
创建对应的Java组件类,继承UIComponentBase并实现NamingContainer,然后通过事件监听器捕获内部input的验证事件,将消息转移到自定义组件。
package com.example; import javax.faces.component.FacesComponent; import javax.faces.component.NamingContainer; import javax.faces.component.UIComponentBase; import javax.faces.component.UIInput; import javax.faces.context.FacesContext; import javax.faces.event.PostValidateEvent; import javax.faces.event.SystemEvent; import javax.faces.event.SystemEventListener; @FacesComponent("com.example.MyCustomComponent") public class MyCustomComponent extends UIComponentBase implements NamingContainer { private UIInput innerInput; @Override public String getFamily() { return NamingContainer.COMPONENT_FAMILY; } public UIInput getInnerInput() { return innerInput; } public void setInnerInput(UIInput innerInput) { this.innerInput = innerInput; // 订阅内部input的验证后事件 innerInput.subscribeToEvent(PostValidateEvent.class, new SystemEventListener() { @Override public void processEvent(SystemEvent event) { // 如果内部input验证通过,直接返回 if (innerInput.isValid()) { return; } FacesContext context = FacesContext.getCurrentInstance(); // 将内部input的所有验证消息转移到自定义组件 innerInput.getMessages().forEach(message -> { // 把消息的关联组件改成当前自定义组件 message.setComponent(MyCustomComponent.this); // 添加消息到上下文,关联自定义组件的客户端id context.addMessage(getClientId(context), message); }); // 清除内部input的消息,避免重复显示 innerInput.getMessages().clear(); } @Override public boolean isListenerForSource(Object source) { return source == innerInput; } }); } }
步骤3:测试你的使用方式
现在你可以按照预期的方式使用组件和消息标签:
<p:outputLabel for="someId" value="Custom input:" /> <cc:myComponent id="someId" value="#{myController.value}"/> <p:message for="someId"/>
当内部<p:inputText>验证失败时,验证消息会显示在指向自定义组件id的<p:message>中,而不是默认的内部input关联消息。
补充说明
- 实现
NamingContainer是关键:它确保自定义组件的客户端id能被JSF正确识别为消息的关联目标。 - 事件监听器的作用:在内部input验证完成后(
PostValidateEvent),我们拦截消息并转移归属,同时清除原消息避免重复显示。
内容的提问来源于stack exchange,提问作者stefan2k
相关产品推荐
相关产品推荐

