2011-03-20 51 views
10

我需要以編程方式在JSF 2中創建複合組件。經過幾天的搜索和實驗,我找出了此方法(受到java.net上的Lexi的啓發):如何以編程方式或動態創建JSF 2中的複合組件

/** 
* 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被設定爲發拋出異常上標記點(< == [此處]):

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被automaticaly產生compositeRoot的ID) 。此外,這些代碼還沒有經過足夠的測試,所以我不確定我是否可以依靠它。

我認爲它非常重要,能夠操縱複合材料部件programmaticaly。否則,複合組件是無用的。

非常感謝。

+0

經過一番測試我也發現​​,我不能編程嵌套任何組件(添加爲兒童)在其他複合組件:(。 – 2011-03-23 22:44:49

+1

好吧所以我最終找到的解決方案。在這裏你可以下載演示豪以編程方式創建複合組件。 /confluence.highsource。org/display/Hifaces20/Dynamic + Faces + - + sample +演示+動態+或+編程+ UI +創建+使用+ JSF + 2.0 +和+ Facelets – 2011-03-24 22:52:20

+0

我試過現在的鏈接和它的工作原理。這個過程非常複雜,因爲它是關於編程組件創建和整個JSF頁面動態構建的整個項目。 – 2012-01-06 16:20:02

回答

7

由於這兩種解決方案都不適合我,我深入瞭解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; 
} 
+0

當有'所依賴的'時,這會失敗。 – BalusC 2013-04-08 15:47:08

14

我不能詳細解釋具體的問題,但我只能觀察和確認,如問題所示的方法是笨拙和緊耦合到鑽嘴魚科。有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 <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); 
    } 
} 

試想一下,你要包括URI xmlns:my="http://java.sun.com/jsf/composite/mycomponents"<my:testComposite id="someId">,然後用它如下:

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

這也被添加到JSF工具庫OmniFacesComponents#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"); 
+0

你好。我也面臨這個問題; jsf 2.0的Facelet動態創建示例正確嗎?因爲我在這行中面對NPE'* .getAttributes()。get(FaceletContext.FACELET_CONTEXT_KEY);' – user390525 2017-10-16 00:08:42

相關問題