当前位置:  开发笔记 > 前端 > 正文

JSF使用动态添加的子项保存和自定义组件

如何解决《JSF使用动态添加的子项保存和自定义组件》经验,为你挑选了1个好方法。

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

考虑这个例子:


    
        some content
    

我的自定义组件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)

因此,只有column从我的表移动到PrimeFaces表的是创建此错误.其他列不会重新创建,因为在渲染过程中,我使用存储在其中的实例变量StateHelper来指示已经创建了列.

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

有人可以向我解释一下这种状态保存机制是如何工作的,特别是与动态添加的子节点相结合以及如何避免这种异常?

更新(10.02.2017)

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

我现在看到的行为略有不同(不再有例外),取决于是否启用/禁用部分状态保存以及我用于移动的方法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移动一个自定义的时间显示不同的行为.

在渲染响应阶段移动`column`

primefaces:column在渲染响应阶段(例如,在渲染期间)移动时encodeXxx,一切正常.所有列都按正确的顺序排列,并且正确的值和分页工作完全正常.

使用`PostAddToViewEvent`移动`column`

使用这种方法时,根据@BalusC建议,移动primefaces:column在分页上消失.在PostAddToViewEvent多次调用和column同时处理该事件,但是在渲染移动时,它已经消失了,只有三个以前创建columns为仍然存在.

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



1> BalusC..:

JSF状态管理记住对组件树的动态操作,以便它可以确保它在回发的恢复视图之后与生成帖子形式的先前请求的呈现响应期间完全相同.

你得到的例外,

javax.faces.FacesException:无法删除相同的组件两次:table:additional

基本上告诉你已经将同一个组件两次添加到同一个父组件中.

换句话说,您从组件树中获取了所需的组件,然后将其添加到所需的父组件中.但是,根据给定的例外情况,那时候已经附加到了期望的父母身上!实际上,你执行了无操作.但是JSF会记住每个动态组件在同一视图上通过回发添加/删除,即使它实际上是无操作.那部分可能反过来又是JSF实现本身的一个错误,但是你应该首先在组件处于理想的位置时不要移动组件.

快速解决方法是检查组件的父级UIComponent#getParent()是否不是所需的父级,如果是,则跳过该getChildren().add()调用.

if (!componentToMove.getParent().equals(targetParent)) {
    targetParent.getChildren().add(componentToMove);
}

一个hack就是设置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);
    }
}


将`javax.faces.PARTIAL_STATE_SAVING`设置为false时,一切都按预期工作,不会发生异常.
推荐阅读
凹凸曼00威威_694
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有