JSF 2.0:跨多个视图保留组件状态

Posted

技术标签:

【中文标题】JSF 2.0:跨多个视图保留组件状态【英文标题】:JSF 2.0: Preserving component state across multiple views 【发布时间】:2011-06-04 02:18:31 【问题描述】:

我正在使用 MyFaces 2.0.3 / PrimeFaces 2.2RC2 开发的 Web 应用程序分为内容和导航区域。在使用模板(即<ui:define>)包含在多个页面中的导航区域中,有一些小部件(例如导航树、可折叠面板等)我想跨视图保留组件状态。

例如,假设我在主页上。当我通过单击导航树中的产品导航到产品详细信息页面时,我的 Java 代码使用

触发重定向
navigationHandler.handleNavigation(context, null,
  "/detailspage.jsf?faces-redirect=true")

访问该详细信息页面的另一种方法是直接单击主页上显示的产品预告片。对应的<h:link> 将引导我们进入详细信息页面。

在这两种情况下,我的导航树(PrimeFaces 树组件)和可折叠面板的展开状态都会丢失。我理解这是因为重定向 /h:link 会导致创建新视图。

处理这个问题的最佳方法是什么?我已经在我的项目中使用 MyFaces Orchestra 及其对话范围,但我不确定这是否有任何帮助(因为我必须将小部件的展开/折叠状态绑定到支持 bean...但据我所知,这是不可能的)。有没有办法告诉 JSF 将哪些组件状态传播到下一个视图,假设该视图中存在相同的组件?

我想我可能需要一个指向正确方向的指针。谢谢!


更新 1: 我刚刚尝试将面板和树绑定到会话范围的 bean,但这似乎没有效果。另外,我想我必须手动绑定所有子组件(如果有的话),所以这似乎不是要走的路。

更新 2: 将 UI 组件绑定到非请求范围的 bean 不是一个好主意(请参阅我在下面的评论中发布的链接)。如果没有更简单的方法,我可能不得不进行如下操作:

当面板折叠或树展开时,将当前状态保存在会话范围的支持 bean 中(!= UI 组件本身) 组件的状态存储在地图中。映射键是组件的(希望是)唯一的相对 ID。我不能在这里使用整个绝对组件路径,因为如果视图更改,父命名容器的 ID 可能会更改,假设这些 ID 是通过编程方式生成的。 构建新视图后,立即从地图中检索组件的状态并将它们应用到组件。例如,在面板的情况下,我可以将 collapsed 属性设置为从会话范围的后备 bean 检索到的值。

更新 3: 我得到了它如上所述的工作。综上所述,解决方案是将相关属性存储在会话范围的 bean 中,而不是将整个 UIComponent 设置为会话范围。然后,当导航发生后重新构建组件时,通过检索保存的属性(使用EL)来设置属性值,例如

<p:panel collapsed="#backingBean.collapsedState" ... />

(这是一个简化的示例。由于我使用多个面板,因此我使用映射来存储这些属性,如上所述)。

【问题讨论】:

【参考方案1】:

一种解决方案是使用会话范围的 bean。

【讨论】:

将 UI 组件绑定到会话范围的 bean 并 NOT 起作用。这会在我刷新页面后立即导致 IllegalStateException:Component ID j_idt12:j_idt13:j_idt14 has already been found in the view. Correction: 我刚刚使用独立面板再次测试了它,现在它可以工作了。现在让我们弄清楚为什么它不适用于我的其他面板...... 好的,因此将组件绑定到会话范围的 bean 显然是不行的。请参阅 primefaces.prime.com.tr/forum/… 和 myfaces.apache.org/orchestra/myfaces-orchestra-core/…:“为了避免组件和 bean 之间的生命周期不匹配,任何存储组件绑定的 bean 都必须是请求范围的。” 通过仅保存会话范围 bean 中的相关属性(而不是使整个 UIComponent 会话范围)来使其工作。请参阅上面的更新。【参考方案2】:

您所说的可折叠面板是什么意思?我问是因为有一个可关闭的组件和一个组件。我在我的项目的导航窗格中使用。 accordianPanel 有一个名为“activeIndex”的属性。以下是我在 sessionBean 中为维护手风琴标签状态所做的操作:

 private int tabIndex; //declared a private variable

    public SessionBean() 
       tabIndex = 100; //set the initial tab index to 100 so all tabs are closed when page loads.
    

    public int getTabIndex()
       return tabIndex;
    

    public void setTabIndex(int tabIndex)
       this.tabIndex=tabIndex;
    

in my navigation pane:



<p:accordionPanel activeIndex="#sessionBean.tabIndex" collapsible="true" autoHeight="false">
    <p:tab title="#tab1_title">
       <h:commandLink value="link here" action="target_page?faces-redirect=true" /><br/>
    </p:tab>
    <p:tab title="#tab2_title">
       <h:commandLink value="link here" action="target_page?faces-redirect=true" />
    </p:tab>
    <p:tab title="#tab3_title">
       <h:commandLink value="link here" action="target_page?faces-redirect=true" />
    </p:tab>
 </p:accordionPanel>

我没有使用树形组件进行导航,因为它给我的项目带来了一些困难,这些困难可以通过使用手风琴面板轻松克服,所以我无法谈论您导航的那部分。

【讨论】:

对于“可折叠面板”,我的意思是常规的&lt;p:panel&gt; 实例。所以我的面板解决方案与您的类似,即从会话范围的支持 bean 中检索 collapsed 值:&lt;p:panel collapsed="#backingBean.collapsedState" toggleListener="#backingBean.onToggle"&gt;。这里唯一的区别是您还需要一个切换侦听器来更新支持 bean 中的折叠状态,因为否则折叠操作将不会传播到服务器端(因为它是客户端 javascript)。 对于树,方法类似;在这里,您需要选择、展开和折叠侦听器来维护服务器端的状态,而不是切换侦听器。在这些侦听器中,只需更新树节点(例如treeNode.setExpanded(true))即可将客户端状态传播到服务器。下次在页面重新加载时重新构建树时,树节点中的这些值将用于初始化新树。但是,我应该注意到,在 PrimeFaces 2.2RC2 中,展开和折叠侦听器似乎被破坏了。 我会记住这一点,因为我们宁愿在导航系统中使用树。感谢您的提示。

以上是关于JSF 2.0:跨多个视图保留组件状态的主要内容,如果未能解决你的问题,请参考以下文章

在哪里放置组合组件?(JSF 2.0)

JSF 2.0 web.xml 错误页面状态码

android如何跨片段分离/附加保留视图状态

JSF 2.0 动态删除组件

JSF 2.0 应用程序的水平缩放

JSF 2.0 复合组件 - ajax 渲染参数 OUTSIDE 组件定义