如何以编程方式或动态创建JSF 2中的复合组件

我需要以编程方式在JSF 2中创建复合组件。经过几天的搜索和实验,我找到了这种方法(在java.net上受到Lexi的启发):

/** * Method will attach composite component to provided component * @param viewPanel parent component of newly created composite component */ public void setComponentJ(UIComponent viewPanel) { FacesContext context = FacesContext.getCurrentInstance(); viewPanel.getChildren().clear(); // load composite component from file Resource componentResource = context.getApplication().getResourceHandler().createResource("whatever.xhtml", "components/form"); UIComponent composite = context.getApplication().createComponent(context, componentResource); // push component to el composite.pushComponentToEL(context, composite); boolean compcompPushed = false; CompositeComponentStackManager ccStackManager = CompositeComponentStackManager.getManager(context); compcompPushed = ccStackManager.push(composite, CompositeComponentStackManager.StackType.TreeCreation); // Populate the component with value expressions Application application = context.getApplication(); composite.setValueExpression("value", application.getExpressionFactory().createValueExpression( context.getELContext(), "#{stringValue.value}", String.class)); // Populate the component with facets and child components (Optional) UIOutput foo = (UIOutput) application.createComponent(HtmlOutputText.COMPONENT_TYPE); foo.setValue("Foo"); composite.getFacets().put("foo", foo); UIOutput bar = (UIOutput) application.createComponent(HtmlOutputText.COMPONENT_TYPE); bar.setValue("Bar"); composite.getChildren().add(bar); // create composite components Root UIComponent compositeRoot = context.getApplication().createComponent(UIPanel.COMPONENT_TYPE); composite.getAttributes().put(Resource.COMPONENT_RESOURCE_KEY, componentResource); compositeRoot.setRendererType("javax.faces.Group"); composite.setId("compositeID"); try { FaceletFactory factory = (FaceletFactory) RequestStateManager.get(context, RequestStateManager.FACELET_FACTORY); Facelet f = factory.getFacelet(componentResource.getURL()); f.apply(context, compositeRoot); //<==[here] } catch (Exception e) { log.debug("Error creating composite component!!", e); } composite.getFacets().put( UIComponent.COMPOSITE_FACET_NAME, compositeRoot); // attach composite component to parent componet viewPanel.getChildren().add(composite); // pop component from el composite.popComponentFromEL(context); if (compcompPushed) { ccStackManager.pop(CompositeComponentStackManager.StackType.TreeCreation); } } 

问题是,只有当javax.faces.PROJECT_STAGE设置为PRODUCTION时,此代码才适用于我(我花了一整天才弄明白这一点)。 如果将javax.faces.PROJECT_STAGE设置为DEVELOPMENT,则在标记点(<== [here])上抛出异常:

 javax.faces.view.facelets.TagException: /resources/components/form/pokus.xhtml @8,19  Component Not Found for identifier: j_id2.getParent(). at com.sun.faces.facelets.tag.composite.InterfaceHandler.validateComponent(InterfaceHandler.java:135) at com.sun.faces.facelets.tag.composite.InterfaceHandler.apply(InterfaceHandler.java:125) at javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:98) at com.sun.faces.facelets.compiler.NamespaceHandler.apply(NamespaceHandler.java:93) at com.sun.faces.facelets.compiler.EncodingHandler.apply(EncodingHandler.java:82) at com.sun.faces.facelets.impl.DefaultFacelet.apply(DefaultFacelet.java:152) at cz.boza.formcreator.formcore.Try.setComponentJ(Try.java:83) at cz.boza.formcreator.formcore.FormCreator.(FormCreator.java:40) at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:532) 

在compositeRoot组件中设置父组件有一些问题(j_id2是自动生成的compositeRoot ID)。 此代码也没有经过足够的测试,所以我不确定我是否可以依赖它。

我认为能够以编程方式操作复合组件非常重要。 否则复合材料组件是无用的。

非常感谢你。

我无法详细解释具体问题,但我只能观察并确认问题中显示的方法是笨拙且紧密耦合到Mojarra。 有com.sun.faces.*需要Mojarra的特定依赖项。 这种方法不使用标准的API方法,另外,也不适用于像MyFaces这样的其他JSF实现。

这是一种使用标准API提供的方法的简单方法。 关键是您应该使用FaceletContext#includeFacelet()在给定父级中包含复合组件资源。

 public static void includeCompositeComponent(UIComponent parent, String libraryName, String resourceName, String id) { // Prepare. FacesContext context = FacesContext.getCurrentInstance(); Application application = context.getApplication(); FaceletContext faceletContext = (FaceletContext) context.getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY); // This basically creates  based on . Resource resource = application.getResourceHandler().createResource(resourceName, libraryName); UIComponent composite = application.createComponent(context, resource); composite.setId(id); // Mandatory for the case composite is part of UIForm! Otherwise JSF can't find inputs. // This basically creates . UIComponent implementation = application.createComponent(UIPanel.COMPONENT_TYPE); implementation.setRendererType("javax.faces.Group"); composite.getFacets().put(UIComponent.COMPOSITE_FACET_NAME, implementation); // Now include the composite component file in the given parent. parent.getChildren().add(composite); parent.pushComponentToEL(context, composite); // This makes #{cc} available. try { faceletContext.includeFacelet(implementation, resource.getURL()); } catch (IOException e) { throw new FacesException(e); } finally { parent.popComponentFromEL(context); } } 

想象一下,您想要从URI xmlns:my="http://java.sun.com/jsf/composite/mycomponents"包含 ,然后按如下方式使用它:

 includeCompositeComponent(parent, "mycomponents", "testComposite.xhtml", "someId"); 

这也被添加到JSF实用程序库OmniFaces作为Components#includeCompositeComponent() (自V1.5起)。


自JSF 2.2以来的更新ViewDeclarationLanguage类获得了一个新的createComponent()方法,它采用taglib URI和标记名称,也可用于此目的。 因此,如果您使用的是JSF 2.2,则该方法应按如下方式完成:

 public static void includeCompositeComponent(UIComponent parent, String taglibURI, String tagName, String id) { FacesContext context = FacesContext.getCurrentInstance(); UIComponent composite = context.getApplication().getViewHandler() .getViewDeclarationLanguage(context, context.getViewRoot().getViewId()) .createComponent(context, taglibURI, tagName, null); composite.setId(id); parent.getChildren().add(composite); } 

想象一下,您想要从URI xmlns:my="http://xmlns.jcp.org/jsf/composite/mycomponents"包含 ,然后按如下方式使用它:

 includeCompositeComponent(parent, "http://xmlns.jcp.org/jsf/composite/mycomponents", "testComposite", "someId"); 

由于这两种解决方案对我都不起作用,因此我深入研究了JSF实现,以了解如何在组件树中添加和处理静态插入的组合。 这是我最终得到的工作代码:

 public UIComponent addWidget( UIComponent parent, String widget ) { UIComponent cc = null; UIComponent facetComponent = null; FacesContext ctx = FacesContext.getCurrentInstance(); Resource resource = ctx.getApplication().getResourceHandler().createResource( widget + ".xhtml", "widgets" ); FaceletFactory faceletFactory = (FaceletFactory) RequestStateManager.get( ctx, RequestStateManager.FACELET_FACTORY ); // create the facelet component cc = ctx.getApplication().createComponent( ctx, resource ); // create the component to be populated by the facelet facetComponent = ctx.getApplication().createComponent( UIPanel.COMPONENT_TYPE ); facetComponent.setRendererType( "javax.faces.Group" ); // set the facelet's parent cc.getFacets().put( UIComponent.COMPOSITE_FACET_NAME, facetComponent ); // populate the facetComponent try { Facelet facelet = faceletFactory.getFacelet( resource.getURL() ); facelet.apply( ctx, facetComponent ); } catch ( IOException e ) { e.printStackTrace(); } // finally add the facetComponent to the given parent parent.getChildren().add( cc ); return cc; }