使用 ui:repeat 的复合组件调用方法中缺少参数值
Missing parameter values in invoked method with composite components using ui:repeat
所以经过几天的调试,我们最终能够在 JSF 中重现一些我们不理解的复合组件、ui:repeat
、p:remoteCommand
和部分状态保存之间的奇怪交互。
场景
复合组件使用 ui:repeat
遍历对象列表。在每次迭代期间,包含另一个复合组件并传递参数。
<ui:composition (...)>
<ui:repeat var="myVar" value="#{cc.attrs.controller.someList}">
<namespace:myRemoteCommand someParam="SomeParam"/>
在包含的复合组件中,有一个自动 运行 p:remoteCommand
使用组件接口中定义的参数调用方法。
<ui:component (...)>
<p:remoteCommand actionListener="#{someBean.someMethod(cc.attrs.someParam)}"
autoRun="true"
async="true"
global="false">
但是,在someMethod(...)
中设置断点时,会传递一个空字符串。这仅在 部分状态保存 设置为 false 时发生。
解决方案
我们尝试了几种解决方案,以下似乎有效(但我们不明白原因,也无法预见可能发生的任何进一步问题):
我们可以设置部分状态保存为true
.
我们可以将复合组件模式改为ui:include
。
我们可以删除一个或两个复合组件,直接包含内容。
问题
为什么 JSF 会这样?复合组件 ui:repeat
和参数传递之间的这种交互是什么,它根据我们是否使用 ui:include
/ 部分状态保存而改变?
我们正在使用 Primefaces 5.3、Glassfish 4.1、Mojarra 2.2.12、Java 8.
你的代码没问题。只是Mojarra的<ui:repeat>
坏了。您不是第一个面临 <ui:repeat>
.
状态管理相关问题的人
- Checkbox inside ui:repeat not refreshed by Ajax
- Dynamically added input field in ui:repeat is not processed during form submit
- Components are with the same id inside ui:repeat
- <h:form> within <ui:repeat> not entirely working, only the last <h:form> is processed
- Composite component with custom backing component breaks strangely when nested inside ui:repeat
- ui:repeat in o:tree not working as expected
你的问题的根本原因是 #{cc}
在 <ui:repeat>
需要访问树的那一刻不可用。实际上,<ui:repeat value>
是 null
。一个快速的解决方法是使用 pushComponentToEL(facesContext, null)
.
显式地推送 UIRepeat#visitTree()
method. Given Mojarra 2.2.12, add below lines right before line 734 中的 #{cc}
UIComponent compositeParent = getCompositeComponentParent(this);
if (compositeParent != null) {
compositeParent.pushComponentToEL(facesContext, null);
}
并在 line 767 之后添加下面几行 popComponentFromEL(facesContext)
。
if (compositeParent != null) {
compositeParent.popComponentFromEL(facesContext);
}
如果您不是从源代码构建 Mojarra,请复制整个 source code of UIRepeat
into your project, maintaining its package structure and apply above changes on it. Classes in /WEB-INF/classes
have higher classloading precedence than those in /WEB-INF/lib
and server's /lib
. I have at least created issue 4162 来解决这个问题。
另一种方法是将 Mojarra 替换为 MyFaces,或者将 <ui:repeat>
替换为具有状态管理权限的基于 UIData
的组件,例如 <h:dataTable>
或 <p:dataList>
.
<p:dataList type="none" var="myVar" value="#{cc.attrs.controller.someList}">
<namespace:myRemoteCommand someParam="SomeParam" />
</p:dataList>
您可能只想应用一些 CSS 来摆脱小部件样式(边框等),但那是 trivial。
另请参阅:
- Should PARTIAL_STATE_SAVING be set to false?
所以经过几天的调试,我们最终能够在 JSF 中重现一些我们不理解的复合组件、ui:repeat
、p:remoteCommand
和部分状态保存之间的奇怪交互。
场景
复合组件使用 ui:repeat
遍历对象列表。在每次迭代期间,包含另一个复合组件并传递参数。
<ui:composition (...)>
<ui:repeat var="myVar" value="#{cc.attrs.controller.someList}">
<namespace:myRemoteCommand someParam="SomeParam"/>
在包含的复合组件中,有一个自动 运行 p:remoteCommand
使用组件接口中定义的参数调用方法。
<ui:component (...)>
<p:remoteCommand actionListener="#{someBean.someMethod(cc.attrs.someParam)}"
autoRun="true"
async="true"
global="false">
但是,在someMethod(...)
中设置断点时,会传递一个空字符串。这仅在 部分状态保存 设置为 false 时发生。
解决方案
我们尝试了几种解决方案,以下似乎有效(但我们不明白原因,也无法预见可能发生的任何进一步问题):
我们可以设置部分状态保存为
true
.我们可以将复合组件模式改为
ui:include
。我们可以删除一个或两个复合组件,直接包含内容。
问题
为什么 JSF 会这样?复合组件 ui:repeat
和参数传递之间的这种交互是什么,它根据我们是否使用 ui:include
/ 部分状态保存而改变?
我们正在使用 Primefaces 5.3、Glassfish 4.1、Mojarra 2.2.12、Java 8.
你的代码没问题。只是Mojarra的<ui:repeat>
坏了。您不是第一个面临 <ui:repeat>
.
- Checkbox inside ui:repeat not refreshed by Ajax
- Dynamically added input field in ui:repeat is not processed during form submit
- Components are with the same id inside ui:repeat
- <h:form> within <ui:repeat> not entirely working, only the last <h:form> is processed
- Composite component with custom backing component breaks strangely when nested inside ui:repeat
- ui:repeat in o:tree not working as expected
你的问题的根本原因是 #{cc}
在 <ui:repeat>
需要访问树的那一刻不可用。实际上,<ui:repeat value>
是 null
。一个快速的解决方法是使用 pushComponentToEL(facesContext, null)
.
UIRepeat#visitTree()
method. Given Mojarra 2.2.12, add below lines right before line 734 中的 #{cc}
UIComponent compositeParent = getCompositeComponentParent(this);
if (compositeParent != null) {
compositeParent.pushComponentToEL(facesContext, null);
}
并在 line 767 之后添加下面几行 popComponentFromEL(facesContext)
。
if (compositeParent != null) {
compositeParent.popComponentFromEL(facesContext);
}
如果您不是从源代码构建 Mojarra,请复制整个 source code of UIRepeat
into your project, maintaining its package structure and apply above changes on it. Classes in /WEB-INF/classes
have higher classloading precedence than those in /WEB-INF/lib
and server's /lib
. I have at least created issue 4162 来解决这个问题。
另一种方法是将 Mojarra 替换为 MyFaces,或者将 <ui:repeat>
替换为具有状态管理权限的基于 UIData
的组件,例如 <h:dataTable>
或 <p:dataList>
.
<p:dataList type="none" var="myVar" value="#{cc.attrs.controller.someList}">
<namespace:myRemoteCommand someParam="SomeParam" />
</p:dataList>
您可能只想应用一些 CSS 来摆脱小部件样式(边框等),但那是 trivial。
另请参阅:
- Should PARTIAL_STATE_SAVING be set to false?