我有一个问题。

我实现了一个 PhaseListener,它的目的是向树中附加有消息的任何 UIInput 组件添加样式类,如果没有附加任何消息,则删除该样式类。

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

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

谢谢,

詹姆士

有帮助吗?

解决方案 2

使用的ViewHandler实现,但是这不是有效的。的PhaseListener在呈现响应相不具有访问该组件树。

其他提示

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

要了解视图构建时间到底是多少,请转到问题 视图构建时间是多少?

您基本上想要挂钩“查看渲染时间”而不是 beforePhaseRENDER_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. 或者,实施一个全球性的 SystemEventListener 为了 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. 或者,挂上 ViewDeclarationLanguage#renderView(), ,但这有点边缘,因为它并不是真正旨在操纵组件树,而是操纵如何渲染视图。


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

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

您最好采用客户端解决方案,而不是操作组件树(这最终会处于 JSF 组件状态!)。想象一下迭代组件中输入的情况,例如 <ui:repeat><h:inputText>. 。树中实际上只有一个输入组件,而不是多个!通过操作样式类 UIInput#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;
    }
});

然后再通过 invalidInputClientIds 类似于 JavaScript 的 JSON 数组,然后通过 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> 正是执行此操作的组件。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top