如何通过导航菜单ajax刷新dynamic包含内容? (JSF SPA)

我只是在学习JSF 2,因为我在这么短的时间内学到了很多东西。

我的问题是关于如何实现所有我的JSF 2页面的通用布局,只有页面的内容部分刷新,而不是整个页面,只要点击不同面板的链接/菜单。 我正在使用Facelets方法,除了每次单击面板中的链接(例如,左侧面板中的菜单项)之外,整个页面都会刷新,所以我只需要它。 我正在寻找的是一种只刷新我的网页内容部分的方法。 为了说明下面是我的目标页面布局。

在这里输入图像描述 没有发布我的代码,因为我不知道如果Facelets可以做到这一点。 除了Facelets之外,还有其他方法更适合我的需求吗?

一个简单的方法将是以下观点:

<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. } 

在这个例子中,实际包含的模板是include3.xhtmlinclude2.xhtmlinclude3.xhtml中的/WEB-INF/includes文件夹(文件夹和位置是完全免费的,您的select;文件只是放在/WEB-INF中为了防止通过猜测浏览器的地址栏中的URL 直接访问 )。

这种方法适用于所有的MyFaces版本,但需要至lessMojarra 2.3.0。 如果您使用的是早于2.3.0的Mojarra版本,那么当<ui:include>页面又包含<h:form>时,这一切都会失败。 任何回发将失败,因为它完全缺less视图状态。 你可以通过升级到最低限度的Mojarra 2.3.0或者在这个答案中find一个脚本来解决这个问题。h :commandButton / h:commandLink不能在第一次点击时工作,只能在第二次点击时工作 ,或者如果你已经在使用JSF实用程序库OmniFaces ,使用它的FixViewState脚本 。 或者,如果您已经在使用PrimeFaces并专门使用<p:xxx> ajax,那么它已经被透明地考虑在内了。

另外,请确保您使用的是最低版本的Mojarra 2.1.18,因为旧版本将会使视图范围的bean活动失败,从而导致在回发期间使用错误的包含。 如果你不能升级,那么你需要回到有条件地渲染视图的下面(相对笨拙的)方法,而不是有条件地构build视图:

 ... <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> 

缺点是视图会变得相对较大,并且所有相关的托pipebean可能会被不必要地初始化,即使它们不会按照呈现的条件使用。

至于元素的定位,这只是应用正确的CSS的问题。 这超出了JSF的范围:)至less, <h:panelGroup layout="block">呈现一个<div> ,所以应该足够好。

最后但并非最不重要的是,这种SPA(单页应用)方法不是SEO友好的。 所有这些页面都不能被searchbots索引,也不能被最终用户collections,你可能需要在客户端使用HTML5历史,并提供一个服务器端回退。 而且,对于带有表单的页面,同一个视图范围的bean实例将在所有页面中被重用,导致在您回到先前访问过的页面时导致不直观的范围行为。 我build议使用模板方法,而不是像这个答案的第二部分所述: 如何使用JSF 2.0 Facelets在XHTML中包含另一个XHTML? 另请参见如何在JSF中导航? 如何使URL反映当前页面(而不是以前的页面) 。

如果只想刷新页面的一部分,则只有两种方法可行(一般来说,不仅仅是JSF)。 你必须使用框架或Ajax。 JSF 2本地支持ajax,检出f:ajax标记只更新一个组件,而不用重新加载整个页面。

Netbeans提供了一个使用JSF以最小的努力创buildbuild议布局的向导。 所以,最好的方法是看看Facelets模板向导 ,看看生成的源代码。