一个简单的方法是以下视图:
<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"; // Default include.
}
// +getter+setter.
}
在这个例子中,实际的包含模板是include1.xhtml
,include2.xhtml
和include3.xhtml
in/WEB-INF/includes
文件夹(文件夹和位置完全由您选择;这些文件只是放置在其中/WEB-INF
以防止通过猜测浏览器地址栏中的 URL 来直接访问)。
此方法适用于所有 MyFaces 版本,但至少需要 Mojarra 2.3.0。如果您使用的是早于 2.3.0 的 Mojarra 版本,那么当<ui:include>
页面又包含<h:form>
. 任何回发都会失败,因为它完全丢失了视图状态。您可以通过升级到最低 Mojarra 2.3.0 或使用此答案中的脚本来解决此问题h:commandButton/h:commandLink 在第一次单击时不起作用,仅在第二次单击时起作用,或者如果您已经在使用 JSF 实用程序库OmniFaces,使用它的FixViewState 脚本。或者,如果您已经在使用 PrimeFaces 并专门使用<p:xxx>
ajax,那么它已经被透明地考虑在内。
此外,请确保您最低限度地使用 Mojarra 2.1.18,因为旧版本将无法保持视图范围 bean 处于活动状态,从而导致在回发期间使用错误的 include。如果您无法升级,那么您需要退回到以下(相对笨拙)有条件地渲染视图而不是有条件地构建视图的方法:
...
<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 可能会被不必要地初始化,即使它们不会根据呈现的条件使用。
至于元素的定位,这只是应用正确的 CSS 的问题。这超出了 JSF 的范围 :) 至少,<h:panelGroup layout="block">
呈现 a <div>
,所以应该足够好。
最后但同样重要的是,这种 SPA(单页应用程序)方法对 SEO 不友好。所有页面都不能被搜索机器人索引,也不能被最终用户收藏,您可能需要在客户端中摆弄 HTML5 历史记录并提供服务器端后备。此外,在带有表单的页面的情况下,相同的视图范围 bean 实例将在所有页面中重用,当您导航回以前访问的页面时,会导致不直观的范围行为。我建议改用模板方法,如本答案第二部分所述:如何使用 JSF 2.0 Facelets 在 XHTML 中包含另一个 XHTML?另请参阅如何在 JSF 中导航?如何使 URL 反映当前页面(而不是上一个页面)。