Primefaces JSF状态保存和自定义组件以及动态添加的子组件

Primefaces JSF状态保存和自定义组件以及动态添加的子组件,primefaces,jsf-2,custom-component,Primefaces,Jsf 2,Custom Component,我正在开发一个JSF自定义组件。此组件用于封装另一个组件(即PrimeFaces表)并向其添加自定义行为。例如,它支持的功能之一是从基础数据或某些属性动态创建PrimeFaces列。此外,它还支持在XHTML中声明额外的PrimeFaces列,这些列也应该添加到封装的PrimeFaces表中 考虑这个例子: 在第一次渲染过程中,这可以正常工作。但是,当我对我的组件执行AJAX更新时,会出现以下异常: javax.faces.FacesException: Cannot remove the s

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

考虑这个例子:

在第一次渲染过程中,这可以正常工作。但是,当我对我的组件执行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)
因此,只有从my表移动到PrimeFaces表的
才会产生此错误。其他列不会被重新创建,因为在渲染期间,我使用存储在
stateheloper
中的实例变量来指示已经创建了列

我知道这个异常是从哪里来的,它与JSF保存完整的组件树有关,当JSF恢复视图时,保存的状态与XHTML不一致。我不知道的是,如何解决这个问题

有人能给我解释一下这种状态保存机制到底是如何工作的,特别是与动态添加的子项一起工作的,以及如何避免这种异常

更新(10.02.2017) 我创建了一个小样本项目,没有以前使用过的其他框架。它可以在我的电脑上找到。以前使用的一个主要框架是在
StateContext
中安装了
addRemovelListeners
,用于重放动态动作。为了避免这对我的问题产生影响,并创建一个可复制的环境,我删除了它们

我现在看到的行为略有不同(不再有例外),这取决于是否启用/禁用了部分状态保存,以及我用于移动
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:table
或是其中一个表的子组件的每个组件,都会出现此警告

部分状态保存已禁用 在禁用部分状态保存的情况下,分页确实可以工作,但根据移动一个自定义
primefaces:column
的时间显示不同的行为

在渲染响应阶段移动“列” 当在渲染响应阶段移动
primefaces:column
时,例如在
encodeXxx
中,一切正常。所有列的顺序和值都正确,分页效果非常好

使用“PostAddToViewEvent”移动“column”` 使用此方法时,根据@BalusC建议,移动的
primefaces:column
在分页时消失。处理此事件时,会多次调用
PostAddToViewEvent
,并移动
,但是在渲染时,它已消失,只有以前创建的三个
列仍在那里


在这一点上,我非常困惑。这是Mojarra或Primefaces的一个bug,还是我做错了什么?这种行为在JSF中也可能发生吗?

JSF状态管理会记住对组件树的动态操作,以确保它在回发的还原视图之后,与生成post表单的前一个请求的呈现响应期间完全相同

你得到的例外

javax.faces.FacesException:无法删除同一组件两次:表:附加

基本上说明您已经向同一父级添加了两次相同的组件

换句话说,您从组件树中获得所需组件,然后将其添加到所需父级。然而,根据给定的例外情况,它当时已连接到所需的父级!实际上,您执行了一个禁止操作。但是JSF通过在同一视图上的回发来记住每个动态组件的添加/删除,即使它实际上是禁止操作。这一部分可能是JSF实现本身的一个缺陷,但首先,当组件在所需的位置时,您不应该在组件周围移动

快速修复方法是通过
UIComponent#getParent()
检查组件的父级,如果它不是所需的父级,则跳过
getChildren().add()
调用

if (!componentToMove.getParent().equals(targetParent)) {
    targetParent.getChildren().add(componentToMove);
}
一种破解方法是设置为
false
,这样JSF就不会记住动态动作

componentToMove.setInView(false);
targetParent.getChildren().add(componentToMove);
componentToMove.setInView(true);

// NOTE: with MyFaces, call setInView() on componentToMove.getParent() instead.
然而,使用此方法时要谨慎,另请参见其

但是,执行组件树操作的最自然的方法是使用
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);
    }
}

答案很简单:使用中的是其他框架的错误。该框架覆盖了JSF的状态保存机制,并将其替换为自定义机制。但是,该实现存在错误,并且没有考虑负责正确保存动态组件操作的
dynamicaddremoveelistener
s。他们修复了这个bug,现在可以正常工作了

然而,我想指出的是,修复我的组件有几点是必要的:

首先,@BalusC为我指出了在自定义JSF组件中移动子组件的正确方法:应该使用
PostAddToView
事件侦听器来完成

componentToMove.setInView(false); targetParent.getChildren().add(componentToMove); componentToMove.setInView(true); // NOTE: with MyFaces, call setInView() on componentToMove.getParent() instead.

@ListenerFor(systemEventClass=PostAddToViewEvent.class)
public class YourComponent extends SomeUIComponent {

@Override
public void processEvent(ComponentSystemEvent event) {
    if (event instanceof PostAddToViewEvent) {
        targetParent.getChildren().add(componentToMove);
    }
}
@ListenerFor(systemEventClass=PostAddToViewEvent.class)
public class YourComponent extends SomeUIComponent {

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