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

问题描述 投票:12回答: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 <cc:interface> 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.<init>(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)。另外,此代码还没有经过充分的测试,因此我不确定是否可以依靠它。

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

java dynamic jsf-2 composite-component
2个回答
16
投票

我无法详细解释具体问题,但是我只能观察并确认问题中所示的方法笨拙且与Mojarra紧密结合。有com.sun.faces.*个特定依赖项需要Mojarra。这种方法没有利用标准的API方法,此外,在MyFaces等其他JSF实现中也无法使用。

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

FaceletContext#includeFacelet()

想象您要从URI 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 <ui:component> based on <composite:interface>. 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 <composite:implementation>. 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); } } 中包含<my:testComposite id="someId">,然后按如下方式使用它:

xmlns:my="http://java.sun.com/jsf/composite/mycomponents"

这也已作为includeCompositeComponent(parent, "mycomponents", "testComposite.xhtml", "someId"); 添加到JSF实用程序库OmniFaces中(自V1.5起。)>]


Update

从JSF 2.2开始,Components#includeCompositeComponent()类获得了一个新的ViewDeclarationLanguage方法,该方法采用了标记库URI和标记名,也可用于此目的。因此,如果您使用的是JSF 2.2,则应按以下步骤进行操作:
createComponent()

想象您要从URI createComponent()中包含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); } ,然后按如下方式使用它:

<my:testComposite id="someId">

7
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.