1

我有一个问题。

我已经实现了一个 PhaseListener,它旨在将样式类添加到树中任何附加了消息的 UIInput 组件,如果没有附加任何消息,则删除样式类。

PhaseListener 在 RENDER_RESPONSE 阶段运行,并且在调试时在 beforePhase 和 afterPhase 方法中都有效。在调试时,我发现 beforePhase 无法访问完整的组件树,但 afterPhase 可以。不过,在 afterPhase 中所做的任何更改都不会呈现。

我该怎么做?我希望这完全是服务器端。

谢谢,

詹姆士

4

2 回答 2

2

JSF 组件树仅在视图构建时间之后可用。该RENDER_RESPONSE阶段不一定是在呈现之前访问完整 JSF 组件树的好时机。在没有任何 的初始 GET 请求期间<f:viewAction>,完整的组件树仅afterPhaseRENDER_RESPONSE. 在回发期间,完整的组件树在 中可用beforePhase,但是,当导航到不同的视图时,它仍然会RENDER_RESPONSE阶段更改,因此任何修改都会丢失。

要了解视图构建时间的确切含义,请转到问题什么是视图构建时间?

您基本上想要挂钩“查看渲染时间”而不是beforePhase阶段RENDER_RESPONSE。JSF 提供了几种方法来连接它:

  1. 在某些主模板中,将preRenderView侦听器附加到<f:view>.

    <f:view ...>
        <f:event type="preRenderView" listener="#{bean.onPreRenderView}" />
        ...
    </f:view>
    
    public void onPreRenderView(ComponentSystemEvent event) {
        UIViewRoot view = (UIViewRoot) event.getSource();
        // The view is the component tree. Just modify it here accordingly.
        // ...
    }        
    
  2. 或者,实现一个全局SystemEventListenerfor PreRenderViewEvent

    public class YourPreRenderViewListener implements SystemEventListener {
    
        @Override
        public boolean isListenerForSource(Object source) {
            return source instanceof UIViewRoot;
        }
    
        @Override
        public void processEvent(SystemEvent event) throws AbortProcessingException {
            UIViewRoot view = (UIViewRoot) event.getSource();
            // The view is the component tree. Just modify it here accordingly.
            // ...
        }
    
    }
    

    要让它运行,请将其注册如下faces-config.xml

    <application>
        <system-event-listener>
            <system-event-listener-class>com.example.YourPreRenderViewListener</system-event-listener-class>
            <system-event-class>javax.faces.event.PreRenderViewEvent</system-event-class>
        </system-event-listener>
    </application>
    
  3. 或者,提供ViewHandler您在其中完成工作的自定义renderView()

    public class YourViewHandler extends ViewHandlerWrapper {
    
        private ViewHandler wrapped;
    
        public YourViewHandler(ViewHandler wrapped) {
            this.wrapped = wrapped;
        }
    
        @Override
        public void renderView(FacesContext context, UIViewRoot view) {
            // The view is the component tree. Just modify it here accordingly.
            // ...
    
            // Finally call super so JSF can do the rendering job.
            super.renderView(context, view);
        }
    
        @Override
        public ViewHandler getWrapped() {
            return wrapped;
        }
    
    }
    

    要让它运行,请在以下位置注册faces-config.xml

    <application>
        <view-handler>com.example.YourViewHandler</view-handler>
    </application>
    
  4. 或者, hook on ViewDeclarationLanguage#renderView(),但这有点边缘,因为它并不是真的打算操纵组件树,而是操纵如何呈现视图。


与具体问题无关,这一切都不是您问题中所述具体功能要求的正确解决方案:

这意味着将样式类添加到树中附加了消息的任何 UIInput 组件,如果没有附加任何消息,则删除样式类

您最好选择客户端解决方案,而不是操纵组件树(最终会进入 JSF 组件状态!)。想象一下在迭代组件中输入的情况,例如<ui:repeat><h:inputText>. 树中实际上只有一个输入组件,而不是多个!操作样式类 viaUIInput#setStyleClass()将在每一轮迭代中呈现。

您最好使用UIViewRoot#visitTree()如下方式访问组件树并收集无效输入组件的所有客户端 ID(这种visitTree()方法将透明地考虑迭代组件):

Set<String> invalidInputClientIds = new HashSet<>();
view.visitTree(VisitContext.createVisitContext(context, null, EnumSet.of(VisitHint.SKIP_UNRENDERED)), new VisitCallback() {

    @Override
    public VisitResult visit(VisitContext context, UIComponent component) {
        if (component instanceof UIInput) {
            UIInput input = (UIInput) component;

            if (!input.isValid()) {
                invalidInputClientIds.add(input.getClientId(context.getFacesContext()));
            }
        }

        return VisitResult.ACCEPT;
    }
});

然后将invalidInputClientIdsJSON 数组的风格传递给 JavaScript,然后 JavaScript 将通过它们获取它们document.getElementById()并更改className属性。

for (var i = 0; i < invalidInputClientIds.length; i++) {
    var invalidInput = document.getElementById(invalidInputClientIds[i]);
    invalidInput.className += ' error';
}

JSF 实用程序库OmniFaces有一个<o:highlight>组件可以做到这一点。

于 2015-11-10T16:47:46.170 回答
0

使用 ViewHandler 实现,但效率不高。渲染响应阶段的 PhaseListener 无权访问组件树。

于 2010-03-23T14:11:08.597 回答