我有一个问题。
我实现了一个 PhaseListener,它的目的是向树中附加有消息的任何 UIInput 组件添加样式类,如果没有附加任何消息,则删除该样式类。
PhaseListener 在 RENDER_RESPONSE 阶段运行,并在调试时在 beforePhase 和 afterPhase 方法中工作。在调试时,我发现 beforePhase 无法访问完整的组件树,但 afterPhase 可以。不过,在 afterPhase 中所做的任何更改都不会呈现。
我该怎么办?我希望这完全是服务器端的。
谢谢,
詹姆斯
I'm having an issue.
I've implemented a PhaseListener, which is meant to add a style class to any UIInput components in the tree that have messages attached to them, and removes the style class if it doesn't have any messages attached to them.
The PhaseListener runs in the RENDER_RESPONSE phase, and does it's work in both the beforePhase and afterPhase methods while debugging. While debugging, I found that beforePhase doesn't have access to the full component tree, but afterPhase does. Any changes done in afterPhase aren't rendered though.
How do I go about this? I want this to be completely server side.
Thanks,
James
发布评论
评论(2)
JSF 组件树仅在视图构建时间之后才可用。
RENDER_RESPONSE
阶段不一定是在渲染之前访问完整 JSF 组件树的好时机。在没有任何
的初始 GET 请求期间,完整的组件树仅在afterPhase
中可用,因为它是在RENDER_RESPONSE
RENDER_RESPONSE 期间构建的代码>.在回发期间,完整的组件树在beforePhase
中可用,但是,当导航到不同的视图时,它仍然会在期间发生更改。 >RENDER_RESPONSE
阶段,因此任何修改都会丢失。要了解视图构建时间到底是多少,请转到问题视图构建时间是多少?
您基本上想要挂钩“查看渲染时间”,而不是
RENDER_RESPONSE
阶段的beforePhase
。 JSF 提供了几种挂接它的方法:在某些主模板中,将
preRenderView
侦听器附加到
。<前><代码>
...
或者,实现一个全局
SystemEventListener
为PreRenderViewEvent
。要使其运行,请按如下方式在
faces-config.xml
中注册它:<前><代码><应用程序>
com.example.YourPreRenderViewListener
<系统事件监听器>
<系统事件类>javax.faces.event.PreRenderViewEvent
或者,提供自定义
ViewHandler
您在其中执行的工作renderView( )
。要使其运行,请在
faces-config.xml
中进行如下注册:<前><代码><应用程序>
com.example.YourViewHandler ;
或者,挂接
ViewDeclarationLanguage#renderView()
< /a>,但这有点边缘,因为它并不是真正旨在操纵组件树,而是操纵如何渲染视图。与具体问题无关,这一切都不是您问题中所述的具体功能要求的正确解决方案:
寻求客户端解决方案,而不是操作组件树(这最终会处于 JSF 组件状态!)。想象一下迭代组件中输入的情况,例如
。树中实际上只有一个输入组件,而不是多个!通过 UIInput#setStyleClass() 操作样式类将在每次迭代中呈现。您最好使用
UIViewRoot#visitTree()
访问组件树,如下所示,并收集无效输入组件的所有客户端 ID(此visitTree()
方法将透明地采用迭代组件考虑):然后将 JSON 数组形式的
invalidInputClientIds
传递给 JavaScript,然后 JavaScript 将通过document.getElementById()
获取它们并更改className< /代码> 属性。
JSF 实用程序库 OmniFaces 有一个
组件正是这样做的。The JSF component tree is only available after the view build time. The
RENDER_RESPONSE
phase is not necessarily a good moment to have access to the full JSF component tree before it gets rendered. During an initial GET request without any<f:viewAction>
, the full component tree is only available in theafterPhase
as it's being built during theRENDER_RESPONSE
. During a postback the full component tree is available in thebeforePhase
, however, when a navigation to a different view has taken place, then it would stil be changed during theRENDER_RESPONSE
phase, so any modifications would get lost.To learn what exactly the view build time is, head to the question What's the view build time?
You basically want to hook on "view render time" rather than
beforePhase
ofRENDER_RESPONSE
phase. JSF offers several ways to hook on it:In some master template, attach a
preRenderView
listener to<f:view>
.Or, implement a global
SystemEventListener
forPreRenderViewEvent
.To get it to run, register it as below in
faces-config.xml
:Or, provide a custom
ViewHandler
wherein you do the job inrenderView()
.To get it to run, register as below in
faces-config.xml
:Or, hook on
ViewDeclarationLanguage#renderView()
, but this is a bit on the edge as it isn't really intented to manipulate the component tree, but to manipulate how to render the view.Unrelated to the concrete problem, this all is not the right solution for the concrete functional requirement as stated in your question:
You'd really better head for a client side solution rather than manipulating the component tree (which would end up in JSF component state!). Imagine the case of inputs in iterating components such as
<ui:repeat><h:inputText>
. There's physically only one input component in the tree, not multiple! Manipulating the style class viaUIInput#setStyleClass()
would get presented in every iteration round.You'd best visit the component tree using
UIViewRoot#visitTree()
as below and collect all client IDs of invalid input components (thisvisitTree()
approach will transparently take iterating components into account):And then thereafter pass
invalidInputClientIds
in flavor of a JSON array to JavaScript which will then grab them viadocument.getElementById()
and alter theclassName
attribute.The JSF utility library OmniFaces has a
<o:highlight>
component which does exactly this.使用 ViewHandler 实现,但效率不高。渲染响应阶段的 PhaseListener 无法访问组件树。
Implemented using a ViewHandler, however it's not efficient. PhaseListener in Render Response phase doesn't have access to the component tree.