gpt4 book ai didi

primefaces - JSF 状态保存和自定义组件与动态添加的子项

转载 作者:行者123 更新时间:2023-12-03 23:45:14 25 4
gpt4 key购买 nike

我正在开发一个 JSF 自定义组件。该组件的目的是封装另一个组件(即 PrimeFaces 表)并向其添加自定义行为。例如,它支持的功能之一是从基础数据或某些属性动态创建 PrimeFaces 列。此外,它支持在 XHTML 中声明额外的 PrimeFaces 列,这些列也应该添加到封装的 PrimeFaces 表中。

考虑这个例子:

<my:table id="table" fields="title,label,value,additional">
<primefaces:column id="additional">
some content
</primefaces:column>
</my:table>

我的自定义组件在渲染期间从 fields 属性动态创建 PrimeFaces 列。然后它将所有 column 子节点移动到 PrimeFaces 表,因此渲染后的组件树如下所示:
my:table id="table"
|---primefaces:table id="table_table"
|---primefaces:column id="title"
|---primefaces:column id="label"
|---primefaces:column id="value"
|---primefaces:column id="additional"

在第一次渲染期间,这工作正常。但是,当我对组件执行 AJAX 更新时,出现以下异常:
javax.faces.FacesException: Cannot remove the same component twice: table:additional
at com.sun.faces.context.StateContext$DynamicAddRemoveListener.handleAddRemoveWithAutoPrune(StateContext.java:761)
at com.sun.faces.context.StateContext$DynamicAddRemoveListener.handleRemove(StateContext.java:629)
at com.sun.faces.context.StateContext$AddRemoveListener.processEvent(StateContext.java:342)
at com.sun.faces.context.StateContext$DynamicAddRemoveListener.processEvent(StateContext.java:565)
at javax.faces.event.SystemEvent.processListener(SystemEvent.java:108)
at javax.faces.event.ComponentSystemEvent.processListener(ComponentSystemEvent.java:118)
at com.sun.faces.application.ApplicationImpl.processListenersAccountingForAdds(ApplicationImpl.java:2218)
at com.sun.faces.application.ApplicationImpl.invokeViewListenersFor(ApplicationImpl.java:2036)
at com.sun.faces.application.ApplicationImpl.publishEvent(ApplicationImpl.java:290)
at com.sun.faces.application.ApplicationImpl.publishEvent(ApplicationImpl.java:245)
at javax.faces.application.ApplicationWrapper.publishEvent(ApplicationWrapper.java:726)
at javax.faces.component.UIComponentBase.disconnectFromView(UIComponentBase.java:2275)
at javax.faces.component.UIComponentBase.doPreRemoveProcessing(UIComponentBase.java:1939)
at javax.faces.component.UIComponentBase.setParent(UIComponentBase.java:437)
at javax.faces.component.UIComponentBase$ChildrenList.remove(UIComponentBase.java:2757)
at com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.adjustIndexOfDynamicChildren(ComponentTagHandlerDelegateImpl.java:283)
at com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:223)
at javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
at javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:95)
at com.sun.faces.facelets.tag.ui.DefineHandler.applyDefinition(DefineHandler.java:106)
at com.sun.faces.facelets.tag.ui.CompositionHandler.apply(CompositionHandler.java:206)
at com.sun.faces.facelets.impl.DefaultFaceletContext$TemplateManager.apply(DefaultFaceletContext.java:395)
at com.sun.faces.facelets.impl.DefaultFaceletContext.includeDefinition(DefaultFaceletContext.java:366)
at com.sun.faces.facelets.tag.ui.InsertHandler.apply(InsertHandler.java:111)
at javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)
at com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:202)
at javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
at javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:95)
at com.sun.faces.facelets.tag.ui.CompositionHandler.apply(CompositionHandler.java:194)
at com.sun.faces.facelets.compiler.NamespaceHandler.apply(NamespaceHandler.java:93)
at com.sun.faces.facelets.compiler.EncodingHandler.apply(EncodingHandler.java:87)
at com.sun.faces.facelets.impl.DefaultFacelet.include(DefaultFacelet.java:312)
at com.sun.faces.facelets.impl.DefaultFacelet.include(DefaultFacelet.java:371)
at com.sun.faces.facelets.impl.DefaultFacelet.include(DefaultFacelet.java:350)
at com.sun.faces.facelets.impl.DefaultFaceletContext.includeFacelet(DefaultFaceletContext.java:199)
at com.sun.faces.facelets.tag.ui.IncludeHandler.apply(IncludeHandler.java:124)
at com.sun.faces.facelets.tag.ui.InsertHandler.apply(InsertHandler.java:116)
at javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:95)
at javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)
at com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:202)
at javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
at javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:95)
at javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)
at com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:202)
at javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
at javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:95)
at com.sun.faces.facelets.tag.ui.DefineHandler.applyDefinition(DefineHandler.java:106)
at com.sun.faces.facelets.tag.ui.CompositionHandler.apply(CompositionHandler.java:206)
at com.sun.faces.facelets.impl.DefaultFaceletContext$TemplateManager.apply(DefaultFaceletContext.java:395)
at com.sun.faces.facelets.impl.DefaultFaceletContext.includeDefinition(DefaultFaceletContext.java:366)
at com.sun.faces.facelets.tag.ui.InsertHandler.apply(InsertHandler.java:111)
at javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:95)
at javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)
at com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:202)
at javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
at javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:95)
at javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)
at com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:202)
at javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
at javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:95)
at com.sun.faces.facelets.tag.jsf.core.ViewHandler.apply(ViewHandler.java:225)
at javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:95)
at javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)
at com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:202)
at javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
at javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:95)
at com.sun.faces.facelets.compiler.NamespaceHandler.apply(NamespaceHandler.java:93)
at com.sun.faces.facelets.compiler.EncodingHandler.apply(EncodingHandler.java:87)
at com.sun.faces.facelets.impl.DefaultFacelet.include(DefaultFacelet.java:312)
at com.sun.faces.facelets.impl.DefaultFacelet.include(DefaultFacelet.java:371)
at com.sun.faces.facelets.impl.DefaultFacelet.include(DefaultFacelet.java:350)
at com.sun.faces.facelets.impl.DefaultFaceletContext.includeFacelet(DefaultFaceletContext.java:199)
at com.sun.faces.facelets.tag.ui.CompositionHandler.apply(CompositionHandler.java:174)
at com.sun.faces.facelets.compiler.NamespaceHandler.apply(NamespaceHandler.java:93)
at com.sun.faces.facelets.compiler.EncodingHandler.apply(EncodingHandler.java:87)
at com.sun.faces.facelets.impl.DefaultFacelet.include(DefaultFacelet.java:312)
at com.sun.faces.facelets.impl.DefaultFacelet.include(DefaultFacelet.java:371)
at com.sun.faces.facelets.impl.DefaultFacelet.include(DefaultFacelet.java:350)
at com.sun.faces.facelets.impl.DefaultFaceletContext.includeFacelet(DefaultFaceletContext.java:199)
at com.sun.faces.facelets.tag.ui.CompositionHandler.apply(CompositionHandler.java:174)
at com.sun.faces.facelets.compiler.NamespaceHandler.apply(NamespaceHandler.java:93)
at com.sun.faces.facelets.compiler.EncodingHandler.apply(EncodingHandler.java:87)
at com.sun.faces.facelets.impl.DefaultFacelet.apply(DefaultFacelet.java:161)
at com.sun.faces.application.view.FaceletViewHandlingStrategy.buildView(FaceletViewHandlingStrategy.java:1006)
at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:99)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:219)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:647)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:230)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at de.gebit.trend.servlet.security.AuthorizationFilter.doFilter(AuthorizationFilter.java:269)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:108)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:620)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:349)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:783)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:789)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1455)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Unknown Source)

因此,只有从我的表移动到 PrimeFaces 表的 column 才会产生此错误。其他列不会重新创建,因为在渲染期间,我使用存储在 StateHelper 中的实例变量来指示已创建列。

我以某种方式理解这个异常是从哪里来的,它与 JSF 保存完整组件树有关,并且当 JSF 恢复 View 时,保存的状态与 XHTML 不一致。我不知道的是,如何解决这个问题。

有人可以向我解释一下这种状态保存机制是如何工作的,特别是与动态添加的 child 一起使用以及如何避免这种异常?

更新 (10.02.2017)

我创建了一个小型示例项目,没有使用之前使用的其他框架。它可以在我的 GitHub profile 上找到。之前使用的一个主要框架是摆弄安装在 AddRemoveListeners 中的 StateContext 以重放动态 Action 。为了避免这对我的问题产生影响并创建一个可重现的环境,我删除了它们。

我现在看到的行为略有不同(不再有异常(exception)),取决于 部分状态保存 是否启用/禁用以及我用来移动 primefaces:column 的方法:

在所有情况下,表格的第一次渲染都可以正常工作。然后我通过提交分页请求来执行回发请求。在某些情况下,这种行为是错误的。

启用部分状态保存

启用部分状态保存后,分页不起作用。我没有得到异常,但有很多类似的警告:
Feb 10, 2017 4:33:11 PM com.sun.faces.application.view.FaceletPartialStateManagementStrategy saveDynamicActions
WARNUNG: Unable to save dynamic action with clientId 'form:table:table_table:additional' because the UIComponent cannot be found

对于在 primefaces:table 中动态创建或移动到 primefaces:column 或作为其中之一的子组件的每个组件,都会出现此警告。

部分状态保存已禁用

禁用部分状态保存后,分页确实可以工作,但会根据移动自定义 primefaces:column 的时间显示不同的行为。

在渲染响应阶段移动“列”

encodeXxx 在渲染响应阶段移动时,例如在 primefaces:column 中,一切正常。所有列的顺序都正确,值正确,分页工作得很好。

使用 `PostAddToViewEvent` 移动 `column`

使用此方法时,根据 @BalusC 建议,移动的 PostAddToViewEvent 在分页时消失。 column 被多次调用,在处理这个事件时 column 被移动,但是在渲染时,它消失了,只有之前创建的三个 ojit_code 仍然存在。

在这一点上,我更加困惑。这是 Mojarra 或 Primefaces 中的错误还是我做错了什么? JSF 甚至可以实现这种行为吗?

最佳答案

JSF 状态管理记住对组件树的动态操作,以便它可以确保它在回发的恢复 View 之后 完全一样就像在生成发布表单的前一个请求的渲染响应期间一样。

你得到的异常(exception),

javax.faces.FacesException: Cannot remove the same component twice: table:additional



基本上告诉你已经将相同的组件两次添加到同一个父级。

换句话说,您从组件树中获取了所需的组件,然后将其添加到所需的父级中。但是,根据给定的异常(exception)情况,当时是 已经附加到所需的父级!实际上,您执行了无操作。但是 JSF 会记住在同一 View 上通过回发添加/删除的每个动态组件,即使它实际上是空操作。这部分反过来可能是 JSF 实现本身的一个错误,但是当组件位于所需的位置时,您首先不应移动组件。

一个快速的解决方法是通过 UIComponent#getParent() 检查组件的父级。如果它不是所需的,如果是,则跳过 getChildren().add()称呼。
if (!componentToMove.getParent().equals(targetParent)) {
targetParent.getChildren().add(componentToMove);
}

黑客将设置 UIComponent#setInView() false这样 JSF 就不会记住动态操作。
componentToMove.setInView(false);
targetParent.getChildren().add(componentToMove);
componentToMove.setInView(true);

// NOTE: with MyFaces, call setInView() on componentToMove.getParent() instead.

然而,使用此方法时要小心,另请参阅其 javadoc .

然而,执行组件树操作的最自然的方式是 postAddtoViewEvent监听器而不是在 encodeXxx() 期间方法。
@ListenerFor(systemEventClass=PostAddToViewEvent.class)
public class YourComponent extends SomeUIComponent {

@Override
public void processEvent(ComponentSystemEvent event) {
if (event instanceof PostAddToViewEvent) {
targetParent.getChildren().add(componentToMove);
}
}

关于primefaces - JSF 状态保存和自定义组件与动态添加的子项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41857584/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com