一个直接的方法是使用以下视图:
<h:panelGroup id="header" layout="block">
<h1>Header</h1>
</h:panelGroup>
<h:panelGroup id="menu" layout="block">
<h:form>
<f:ajax render=":content">
<ul>
<li><h:commandLink value="include1" action="#{bean.setPage('include1')}" /></li>
<li><h:commandLink value="include2" action="#{bean.setPage('include2')}" /></li>
<li><h:commandLink value="include3" action="#{bean.setPage('include3')}" /></li>
</ul>
</f:ajax>
</h:form>
</h:panelGroup>
<h:panelGroup id="content" layout="block">
<ui:include src="/WEB-INF/includes/#{bean.page}.xhtml" />
</h:panelGroup>
使用这个 bean:
@ManagedBean
@ViewScoped
public class Bean implements Serializable {
private String page;
@PostConstruct
public void init() {
page = "include1";
}
}
在这个例子中,实际的包含模板是位于
/WEB-INF/includes
文件夹中的
include1.xhtml
、
include2.xhtml
和
include3.xhtml
(文件夹和位置完全由您自己选择;文件只是放置在
/WEB-INF
中为了
防止通过猜测浏览器地址栏中的URL直接访问)。
此方法适用于所有MyFaces 2.x版本,但在Mojarra中需要至少2.3.x。如果您使用的是早于2.3.0的Mojarra版本,则当
<ui:include>
页面反过来包含
<h:form>
时,所有内容都会失败。任何回发都会失败,因为它完全缺少视图状态。您可以通过升级到至少Mojarra 2.3.0或使用此答案中找到的脚本
h: commandButton / h: commandLink第一次单击不起作用,仅在第二次单击时起作用来解决此问题。或者,如果您已经使用PrimeFaces并且专门使用
<p:xxx>
ajax,则它已经自动考虑到了。
此外,请确保您使用的是至少Mojarra 2.1.18版本,因为旧版本无法保持视图范围bean的活动状态,导致在postback期间使用错误的包含。如果您无法升级,则需要退回到下面(相对笨拙)的条件渲染视图而不是条件构建视图的方法:
...
<h:panelGroup id="content" layout="block">
<ui:fragment rendered="#{bean.page eq 'include1'}">
<ui:include src="include1.xhtml" />
</ui:fragment>
<ui:fragment rendered="#{bean.page eq 'include2'}">
<ui:include src="include2.xhtml" />
</ui:fragment>
<ui:fragment rendered="#{bean.page eq 'include3'}">
<ui:include src="include3.xhtml" />
</ui:fragment>
</h:panelGroup>
缺点是视图会变得相对较大,即使根据渲染条件不使用所有相关的托管bean,也可能会不必要地初始化它们。有关
<ui:include src="#{...}">
与
<x:someComponent rendered="#{...}">
的深入解释,请参见
JSTL in JSF2 Facelets... makes sense?。
至于元素的定位,那只是应用正确的CSS的问题。这超出了JSF的范围 :) 至少,<h:panelGroup layout="block">
呈现一个<div>
,所以应该足够好了。
最后,这种单页应用程序(SPA)方法不利于SEO。所有页面都无法被搜索引擎机器人索引或被终端用户添加书签,您可能需要在客户端调整HTML5历史记录并提供服务器端回退。此外,在具有表单的页面中,相同的视图范围bean实例将在所有页面中重复使用,导致在返回以前访问过的页面时出现不直观的作用域行为。我建议采用模板方法,如本答案的第二部分所述:
如何在JSF 2.0 Facelets中包含另一个XHTML?另请参阅
如何在JSF中导航?如何使URL反映当前页面(而不是上一页)?。
<p:panelMenu>
而不是一个简单的<ul>
菜单,那么我需要在<p:menuitem>
中设置什么属性来呈现内容? - Alvaro Pedraza