如何通过导航菜单 ajax-refresh 动态包含内容? (JSF SPA)

Posted

技术标签:

【中文标题】如何通过导航菜单 ajax-refresh 动态包含内容? (JSF SPA)【英文标题】:How to ajax-refresh dynamic include content by navigation menu? (JSF SPA) 【发布时间】:2011-10-29 20:03:35 【问题描述】:

我刚刚学习 JSF 2,感谢这个网站,我在这么短的时间内学到了很多东西。

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

没有发布我的代码,因为我不确定 Facelets 是否可以做到这一点。除了 Facelets 之外,还有其他方法更适合我的要求吗?

【问题讨论】:

【参考方案1】:

如果你只想刷新页面的一部分,只有两种方法可以去(对于一般的 web,而不仅仅是 JSF)。您必须使用框架或 Ajax。 JSF 2 原生支持 ajax,查看 f:ajax 标记以仅更新 1 个组件而无需重新加载整个页面。

【讨论】:

就我目前的经验而言,这在包含包含表单的内容时效果不佳。 true...可能还需要将整个部分包装在一个表单中,或者将表单包含在 ajaxified 部分中 我更多地指的是使用<ui:include src="#bean.dynamicinclude" /> 语法。由于某种原因,在回发的恢复视图期间解析 src 的方式与在渲染响应期间解析的方式不同,这反过来又导致找不到表单,因此没有处理任何内容。但这也可能只是一个 Mojarra 错误。我不知道。然而。 有趣的是,我尝试了 MyFaces 2.1.1,它只是在渲染响应期间没有显示所需的动态包含。 要使工作 ui:include src="..." 与 facelets 1.1.x 一样,您必须将 web 配置参数 org.apache.myfaces.REFRESH_TRANSIENT_BUILD_ON_PSS 设置为 true。详情请见MYFACES-3271。【参考方案2】:

Netbeans 提供了一个向导,可以使用 JSF 轻松创建建议的布局。因此,最好的开始方法是查看Facelets Template Wizard 并查看生成的源代码。

【讨论】:

ajax 加载/刷新部分怎么样? Netbeans 是否也生成该部分?我不这么认为。 是的,你是对的。Netbeans 不会生成该部分。但它有一个很好的向导来定义布局。 ajax 部分显然必须手动完成。有很多方法可以做到这一点,从使用“rendered”属性到使用 ui:include src="..."。如果您在一个页面上有多个表单并且您需要保持“同步”状态,您可以在 myfaces 上使用此 hack: 见MYFACES-3159【参考方案3】:

一个简单的方法是以下视图:

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

用这个豆子:

@ManagedBean
@ViewScoped
public class Bean implements Serializable 

     private String page;

     @PostConstruct
     public void init() 
         page = "include1"; //  Default include.
     

     // +getter+setter.
 

在这个例子中,实际的包含模板是/WEB-INF/includes文件夹中的include1.xhtmlinclude2.xhtmlinclude3.xhtml(文件夹和位置完全由您选择;文件只是按顺序放置在/WEB-INF中通过猜测浏览器地址栏中的 URL 到prevent direct access)。

此方法适用于所有 MyFaces 版本,但至少需要 Mojarra 2.3.0。如果您使用的是早于 2.3.0 的 Mojarra 版本,那么当 &lt;ui:include&gt; 页面又包含 &lt;h:form&gt; 时,这一切都会失败。任何回发都会失败,因为它完全丢失了视图状态。您可以通过升级到最低限度的 Mojarra 2.3.0 或使用此答案 h:commandButton/h:commandLink does not work on first click, works only on second click 中的脚本来解决此问题,或者如果您已经在使用 JSF 实用程序库 OmniFaces,请使用其 FixViewState script。或者,如果您已经在使用 PrimeFaces 并专门使用 &lt;p:xxx&gt; 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 的范围 :) 至少,&lt;h:panelGroup layout="block"&gt; 呈现了一个 &lt;div&gt;,所以应该足够好了。

最后但同样重要的是,这种 SPA(单页应用程序)方法对 SEO 不友好。所有页面都不能被搜索机器人索引,也不能被最终用户收藏,您可能需要在客户端中摆弄 HTML5 历史记录并提供服务器端后备。此外,对于带有表单的页面,相同的视图范围 bean 实例将在所有页面中重用,导致当您导航回先前访问的页面时,范围界定行为不直观。我建议使用模板方法,而不是如本答案第二部分所述:How to include another XHTML in XHTML using JSF 2.0 Facelets? 另请参阅How to navigate in JSF? How to make URL reflect current page (and not previous one)。

【讨论】:

正是我要找的东西!!!加上实现干净且易于理解。对我来说幸运的是,我计划使用的应用程序更像是一个企业 LAN 应用程序,而且我的用户不需要它对 SEO 友好、可搜索(如果 searchbotable 曾经是一个词):) 和可收藏的,这就是为什么它喜欢为我量身定制的要求。非常感谢。 嗨,感谢 BalusC 的精彩回答。我在这个模板中有一个像 include1 这样的提交表单,我想通过命令按钮转到 include2,但它需要单击两次。有没有办法纠正这个问题? @BulusC:如何将 fixviewstate.js 用于 primeface4( @Sima:它会自动识别 PrimeFaces(特别是:jQuery)。 @BalusC: 如果我有一个 Primefaces &lt;p:panelMenu&gt; 而不是一个简单的 &lt;ul&gt; 菜单,我需要在 &lt;p:menuitem&gt; 中设置什么属性来呈现内容?跨度>

以上是关于如何通过导航菜单 ajax-refresh 动态包含内容? (JSF SPA)的主要内容,如果未能解决你的问题,请参考以下文章

如何将菜单动态添加到底部导航视图?

墨刀原型教程:如何轻松做出菜单导航动态效果

Vue + Nuxt js:如何使用引导导航栏将动态活动类应用于父菜单

easyui 怎么动态生成左侧菜单

读取 DOM(网页)中的位置并相应地更改导航菜单类

最全最详细使用publiccms实现动态可维护的导航菜单栏