根据Netbeans7.1附带的Glassfish3.1.1,Mojarra-2.1.3可用。
我有一个@SessionScoped后备bean Tracker,其中包含一个监听器void reset()。
以下内容在所有使用template.xhtml的XHTML页面的f:metadata中正常工作,例如/block/view.xhtml,该页面还接受查询参数id:
<f:view>
<f:metadata>
<f:viewParam name="id" value="#{blockManager.id}"/>
<f:event type="preRenderView" listener="#{tracker.reset}"/>
</f:metadata>
</f:view>
<ui:composition template="/template.xhtml">
如预期,每当我加载(GET)或重新加载页面时,无论id查询参数是什么,#{tracker.reset}监听器都会被调用(通过调试日志揭示)。
然而,必须在每个XHTML页面中包含f:event很麻烦(我有数百个页面),我首先尝试将其放在template.xhtml的f:metadata中。但是当我这样做时,出现了奇怪的情况。它只在第一次加载/block/view.xhtml时调用#{tracker.reset}(无论id查询参数是什么),之后直到我加载具有不同viewId的另一个页面(例如/actor/view.xhtml、/block/list.xhtml或/index.html)才再次调用。
我在template.xhtml中使用#{facesContext.viewRoot.viewId}检查了viewId。从viewId的角度来看,id查询参数对于区分使用不同id查询参数调用的不同block/view.xhtml?id=[id]页面并没有起到任何作用,viewId始终只是'/block/view.xhtml'。
在撰写本stackoverflow帖子期间,我发现了解决我的问题的方法:只需将f:event放在template.xhtml的f:metadata之外即可(我在template.xhtml中使用f:metadata进行f:event的分组)。这在template.xhtml中有效。
<f:metadata>
..
</f:metadata>
<f:event type="preRenderView" listener="#{tracker.reset}"/>
<h:head>
但我仍有以下问题:
问:在模板中是否将f:event放置在f:metadata内部是否有区别?
我提出这个问题的原因是,在Stackoverflow上有很多例子,既有在template.xhtml中使用f:metadata的例子,也有在模板中使用f:metadata内的f:event的例子。
BalusC在When to use f:viewAction / preRenderView versus PostConstruct?中指出:
preRenderView事件在每个HTTP请求上被调用。
只有当我在最终的XHTML页面的f:metadata中或者在模板的f:metadata之外拥有preRenderView f:event时,它才似乎是真实存在的(按预期工作), 但是如果它位于模板的f:metadata内部,则不起作用。
关于是否应该在模板的f:metadata中具有f:event,或者根本不使用模板的f:metadata,存在一些争议。
JSF2 Complete Reference (Burns and Schalk) p.540指出:
标签封装了一组元素,用于指定Facelets视图的元数据,因此必须是f:view标签的子元素,不能出现在模板中。从JSF2.0开始,此标签的唯一目的是封装f:viewParam标签。但是,在Stackoverflow上有很多f:metadata在模板内使用的示例,也有很多f:event在f:metadata内使用的示例。这里也讨论了这个问题:是否将f:event放置在f:metadata内很重要? BalusC在这里给出了有益的解释:“..<f:event>不一定非要放在<f:metadata>内。它可以附加到任何组件上。..当您有一堆<f:viewParam>并想要挂钩一个<f:event>在所有这些视图参数设置后调用操作时,它确实为纯自我文档目的放置在<f:metadata>内。..”但是我的经验表明,将f:event放置在模板的f:metadata中会产生略微不同(奇怪)的行为。为什么?