Cómo crear mediante progtwigción o dinámica un componente compuesto en JSF 2

Necesito crear programáticamente componentes compuestos en JSF 2. Después de unos días de búsqueda y experimentos, descubro este método (altamente inspirado por Lexi en 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); } } 

El problema es que este código me funciona solo cuando javax.faces.PROJECT_STAGE está establecido en PRODUCCIÓN (Me tomó todo el día resolver esto). Si javax.faces.PROJECT_STAGE está establecido en DEVELOPMENT Exception se lanza en punto marcado (<== [aquí]):

 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) 

Es un problema con el conjunto primario en el componente compositeRoot (j_id2 es el ID generado automáticamente de compositeRoot). Además, este código no se ha probado lo suficiente, por lo que no estoy seguro de poder confiar en él.

Creo que es muy importante poder manipular componentes compuestos programáticamente. De lo contrario, los componentes compuestos son medio inútiles.

Muchas gracias.

No puedo explicar el problema concreto en detalle, pero solo puedo observar y confirmar que el enfoque que se muestra en la pregunta es torpe y ajustado a Mojarra. Hay com.sun.faces.* Dependencias específicas que requieren Mojarra. Este enfoque no utiliza los métodos API estándar y, además, no funcionaría en otras implementaciones JSF como MyFaces.

Aquí hay un enfoque mucho más simple que utiliza métodos estándar proporcionados por API. El punto clave es que debe usar FaceletContext#includeFacelet() para incluir el recurso componente compuesto en el padre dado.

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

Imagine que desea incluir de URI xmlns:my="http://java.sun.com/jsf/composite/mycomponents" , luego utilícelo de la siguiente manera:

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

Esto también se ha agregado a la biblioteca de utilidades JSF OmniFaces como Components#includeCompositeComponent() (desde V1.5).


Actualización desde JSF 2.2, la clase ViewDeclarationLanguage obtuvo un nuevo método createComponent() tomando el URI taglib y el nombre de la etiqueta que también podría usarse para este propósito. Entonces, si está utilizando JSF 2.2, el enfoque debe hacerse de la siguiente manera:

 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 que desea incluir de URI xmlns:my="http://xmlns.jcp.org/jsf/composite/mycomponents" , luego siguiente manera:

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

Como las dos soluciones no me funcionaron, exploro la implementación de JSF para descubrir cómo se agregan y manejan los compuestos insertados estáticamente en el árbol de componentes. Este es el código de trabajo con el que finalmente terminé:

 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; } 
Intereting Posts