如何在JSF 2中以编程方式或动态创建复合组件 [英] How to programmatically or dynamically create a composite component in JSF 2

查看:191
本文介绍了如何在JSF 2中以编程方式或动态创建复合组件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  / ** 
*方法将复合组件附加到提供的组件
* @param viewPanel新创建的组合组件的父组件
* /
public void setComponentJ(UIComponent viewPanel){
FacesContext context = FacesContext.getCurrentInstance();
viewPanel.getChildren()。clear();

//从文件
加载复合组件资源componentResource = context.getApplication()。getResourceHandler()。createResource(whatever.xhtml,components / form);
UIComponent composite = context.getApplication()。createComponent(context,componentResource);

//将组件推送到el
composite.pushComponentToEL(context,composite);
boolean compcompPushed = false;
CompositeComponentStackManager ccStackManager = CompositeComponentStackManager.getManager(context);
compcompPushed = ccStackManager.push(composite,CompositeComponentStackManager.StackType.TreeCreation);

//使用值表达式填充组件
应用程序应用程序= context.getApplication();
composite.setValueExpression(value,application.getExpressionFactory()。createValueExpression(
context.getELContext(),#{stringValue.value},
String.class));

//使用facet和子组件填充组件(可选)
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);

//创建复合组件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(创建复合组件的错误!!,e);
}
composite.getFacets()。put(
UIComponent.COMPOSITE_FACET_NAME,compositeRoot);

//将复合组件附加到父组件
viewPanel.getChildren()。add(composite);

//来自el
的pop组件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>未找到组件标识符:j_id2.getParent()。 
在com.sun.faces.facelets.tag.composite.InterfaceHandler.validateComponent(InterfaceHandler.java:135)
在com.sun.faces.facelets.tag.composite.InterfaceHandler.apply(InterfaceHandler。 java:125)
在javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:98)
在com.sun.faces.facelets.compiler.NamespaceHandler.apply(NamespaceHandler.java: 93)
在com.sun.faces.facelets.compiler.EncodingHandler.apply(EncodingHandler.java:82)
在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.newIn stance(DelegatingConstructorAccessorImpl.java:45)
在java.lang.reflect.Constructor.newInstance(Constructor.java:532)

在compositeRoot组件中,父设置有一些问题(j_id2是automaticRoot的自动生成ID)。此外,这段代码没有足够的测试,所以我不知道我是否可以依靠它。



我认为能够操纵复合组件程序设计非常重要。否则复合组件是无用的。



非常感谢。

解决方案

p>我不能详细解释具体问题,但我只能观察并确认问题所示的方法是笨拙而紧密耦合到Mojarra。有 com.sun.faces。* 需要Mojarra的特定依赖项。这种方法没有利用标准的API方法,而且在其他JSF实现(如MyFaces)中也不起作用。



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

  public static void includeCompositeComponent(UIComponent parent,String libraryName,String resourceName,String id){
//准备。
FacesContext context = FacesContext.getCurrentInstance();
应用程序应用程序= context.getApplication();
FaceletContext faceletContext =(FaceletContext)context.getAttributes()。get(FaceletContext.FACELET_CONTEXT_KEY);

//这基本上创建了< ui:component>基于< composite:interface> ;.
资源资源= application.getResourceHandler()。createResource(resourceName,libraryName);
UIComponent composite = application.createComponent(context,resource);
composite.setId(id); //对于case复合属性,必须是UIForm的一部分!否则JSF找不到输入。

//这基本上创建了< composite:implementation> ;.
UIComponent implementation = application.createComponent(UIPanel.COMPONENT_TYPE);
implementation.setRendererType(javax.faces.Group);
composite.getFacets()。put(UIComponent.COMPOSITE_FACET_NAME,implementation);

//现在在给定的父项中包含复合组件文件。
parent.getChildren()。add(composite);
parent.pushComponentToEL(context,composite); //这使#{cc}可用。
try {
faceletContext.includeFacelet(implementation,resource.getURL());
} catch(IOException e){
throw new FacesException(e);
} finally {
parent.popComponentFromEL(context);
}
}

想像你想包括< my:testComposite id =someId> 从URI xmlns:my =http://java.sun.com/jsf/composite/mycomponents,然后使用它如下:

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

这也被添加到JSF实用程序库 OmniFaces 作为组件#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);
}

想像你想包含< my: testComposite id =someId> 从URI xmlns:my =http://xmlns.jcp.org/jsf/composite/mycomponents,然后使用它如下:

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


I need to programatically create composite components in JSF 2. After few days of searching and experiments I figure out this method (higly inspired by Lexi at java.net):

/**
 * 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);
    }
}

Problem is that this code works for me only when javax.faces.PROJECT_STAGE is set to PRODUCTION (It took me all day to figure this out). If javax.faces.PROJECT_STAGE is set to DEVELOPMENT Exception is thrown on marked point (<==[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)  

It's some problem with parent set in compositeRoot component (j_id2 is automaticaly generated ID of compositeRoot). Also this code is not tested thorougly enough, so I'm not sure if I can rely on it.

I think its very important to be able to manipulate Composite Components programmaticaly. Otherwise Composite Components are half useless.

Thank you very much.

解决方案

I can't explain the concrete problem in detail, but I can only observe and confirm that the approach as shown in the question is clumsy and tight coupled to Mojarra. There are com.sun.faces.* specific dependencies requiring Mojarra. This approach is not utilizing the standard API methods and, additionally, would not work in other JSF implementations like MyFaces.

Here's a much simpler approach utilizing standard API-provided methods. The key point is that you should use FaceletContext#includeFacelet() to include the composite component resource in the given parent.

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);
    }
}

Imagine that you want to include <my:testComposite id="someId"> from URI xmlns:my="http://java.sun.com/jsf/composite/mycomponents", then use it as follows:

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

This has also been added to JSF utility library OmniFaces as Components#includeCompositeComponent() (since V1.5).


Update since JSF 2.2, the ViewDeclarationLanguage class got a new createComponent() method taking the taglib URI and tag name which could also be used for this purpose. So, if you're using JSF 2.2, the approach should be done as follows:

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);
}

Imagine that you want to include <my:testComposite id="someId"> from URI xmlns:my="http://xmlns.jcp.org/jsf/composite/mycomponents", then use it as follows:

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

这篇关于如何在JSF 2中以编程方式或动态创建复合组件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆