【发布时间】:2012-04-07 21:58:33
【问题描述】:
我遇到了 Spring Webflow 中 primefaces 组件的持久性问题。如果将组件作为原始页面请求的一部分进行实例化,则一切正常。但是,如果在 ajax 请求期间实例化组件,它们不会正确持久化,并且稍后检索它们时,我们会得到一个空对象。这样做的效果是,例如选项卡视图的第二个选项卡上的组件(动态=true)不起作用。一个例子:
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui"
xmlns:c="http://java.sun.com/jsp/jstl/core">
<h:head>
<ui:insert name="headIncludes" />
</h:head>
<h:body>
<h:form id="testForm">
<p:tabView id="myTabView" dynamic="true">
<p:tab title="Tab One" id="tabOne">
<h:panelGroup id="tabOneGroup">
<p:commandLink id="linkX" value="Link X" actionListener="#{sandboxBean.testCommandLinkx}" >
<f:ajax />
</p:commandLink>
</h:panelGroup>
</p:tab>
<p:tab title="Tab Two" id="tabTwo">
<h:panelGroup id="tabTwoGroup">
<p:commandLink id="linkY" value="Link Y" actionListener="#{sandboxBean.testCommandLinky}" >
<f:ajax /><!-- Only matters if we switch to h:commandLink -->
</p:commandLink>
</h:panelGroup>
</p:tab>
</p:tabView>
</h:form>
</h:body>
</html>
当页面第一次加载时,代表 commandlink linkX 的 UIComponent 会被实例化,并且 'actionListener' 的属性值会被保存。当我们单击“Link X”时,该属性值会再次被检索并用于调用 testCommandLinkx()。这很好用。
当我们切换到 Tab 2 时,会发出一个 ajax 请求,并且在此期间,另一个代表 linkY 的 UIComponent 会以相同的方式创建并保持不变。但是,当我们实际点击commandlink并尝试检索actionListener属性值时,发现并没有这个属性。相反,我们会收到一个全新的对象。
如果我们将 tabView 更改为 dynamic="false",则在初始页面请求期间会创建并保留两个命令链接,并且一切正常。如果我们保持 dynamic="true" 但使用 h:commandLink 而不是 p:commandLink,那么一切正常。
现在,我真的不认为这是一个 primefaces 错误,好像很多其他人也会注意到它一样。其他人也已经毫无问题地测试了这个特定的代码片段。出于同样的原因,我怀疑这是一个 webflow 错误。它似乎更有可能是我们项目中的配置错误,但我不知道在哪里。包含每个配置文件似乎有点冗长,但如果您想查看其中的内容,只需评论,我会添加它。
至于人们无疑会问的不同组件的版本:
- Primefaces:3.0.1 和所有更高版本的动态 tabViews(因为这是第一个真正支持它的版本)但如果您使用另一个在 ajax 请求中实例化组件的构造,则可以在早期版本中重新创建它。
- Mojarra:2.0.4(但我也尝试过 2.0.8 和 2.1.6 没有改进)
- Spring Webflow:2.2.1(也试过2.3)
- Tomcat:7.0.12(也尝试过其他各种)
【问题讨论】:
-
尝试将
<h:form>改为<p:tab>。此外,那些<f:ajax/>标签是不必要的。默认情况下,所有 PrimeFaces 命令链接/按钮都已被 ajaxified,这应该由接受布尔值的ajax属性配置。 -
切换表单并不会改变任何东西,我担心。 p:commandLink 不需要
,但它确保在切换到 h:commandLink 时功能相同。我不希望人们认为 h:commandLink 起作用的原因是它不是 ajax 请求。 -
你能详细说明一下“但是,当我们实际点击commandlink并尝试检索actionListener属性值时,我们发现没有这样的属性。相反,我们收到了一个全新的对象。” 部分?你的意思是从动作监听器方法中的视图根(或动作事件参数)检索组件,然后尝试通过
getValue()从中获取命令组件的值?如果有,为什么? -
我自己并没有做任何特别的事情,这只是 primefaces 和 jsf 代码的内部工作。 PrimeFaces CommandLink 类扩展了 jsf 类 UIComponent(通过 UIComponentBase、UICommand 和 HtmlCommandLink)。这个 UIComponent 类有一个 StateHelper 对象,其中实际的属性值存储在地图中。当 p:commandLink 被渲染时,StateHelper.add("actionListener", "#{sandboxBean.testCommandLinkx}" ) 被调用。但是,当我们点击 commandLink 并调用 StateHelper.get("actionListener") 时,由于属性映射为空,因此返回 null。
-
如前所述,只有在 ajax 请求期间呈现 p:commandLink 时才会发生这种情况。如果在整个页面请求期间呈现,则单击链接时 StateHelper.get("actionListener") 正确返回持久值。
标签: jsf jsf-2 primefaces spring-webflow