Hi 2013/1/17 Paul Nicolucci <[email protected]>: > Hello Everyone, > > I was wondering if it is possible to programmatically add a composite > component to a page just as you can a regular UI component. >
This is a tough question, so I'll split it into a few additional questions. - Is there any method right now available to create programmatically a composite component? No, there is not. The entry point for create a composite component is located on: org.apache.myfaces.view.facelets.tag.composite.CompositeComponentResourceTagHandler But the code has not been designed to be called in that way, so try to invoke this class will not work. - It is possible just calling some code in myfaces to create a composite component programatically? No. The big problem is how to set the ids (facelet ids and component ids). At the end, the code will break with duplicate id exceptions or the state will not work as expected. - It is possible to modify facelets code to allow it in a myfaces specific way? Yes, in theory it can be done, but you need to modify a lot of code to get it. In JSF 2.2 it was discussed the inclusion of FaceletFactory into the spec, but at the end it was removed because programatically added components should not follow the same rules as components created by facelets, and with the introduction of PSS algorithm, things that worked before in that part by definition does not work now. See: http://java.net/projects/javaserverfaces-spec-public/lists/jsr344-experts/archive/2012-11/message/91 for details. Note the necessary changes needs to be done inside MyFaces core. - Any suggestions? In my personal opinion, the best is let the dynamic component tree manipulation to facelet components like <c:if ...> or <ui:include src="#{...}">, because those components are integrated with PSS saving algorithm. Just let facelets handle component tree creation/manipulation. In JSF 2.2 Public Review Draft, there is a method in Application class: public UIComponent createComponent(FacesContext context, String taglibURI, String tagName, Map<String,Object> attributes) I suppose this one will do the trick, but in Myfaces 2.2.x branch it has not been implemented yet. regards, Leonardo Uribe > For example: > > Test3.xhtml: > <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" > "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> > > <html xmlns="http://www.w3.org/1999/xhtml" > xmlns:ui="http://java.sun.com/jsf/facelets" > xmlns:h="http://java.sun.com/jsf/html" > xmlns:f="http://java.sun.com/jsf/core" > xmlns:test="http://mycustomlib.com/mylib"> > > <ui:composition template="/WEB-INF/templates/BasicTemplate.xhtml"> > <ui:define name="content"> > <h2>This test uses dynamic component to create composite component</h2> > <h:form> > > <test:dynamic/> > </h:form> > </ui:define> > </ui:composition> > </html> > > mylib.taglib.xml: > <facelet-taglib xmlns="http://java.sun.com/xml/ns/javaee" > xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" > xsi:schemaLocation="http://java.sun.com/xml/ns/javaee > http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd" > version="2.0"> > <namespace>http://mycustomlib.com/mylib</namespace> > <composite-library-name>mylib</composite-library-name> > > <tag> > <tag-name>dynamic</tag-name> > <component> > <component-type>DynamicComponent</component-type> > </component> > </tag> > > </facelet-taglib> > > Important web.xml parameters set: > > <context-param> > > <param-name>javax.faces.FACELETS_LIBRARIES</param-name> > > <param-value>/WEB-INF/mylib.taglib.xml</param-value> > > </context-param> > > <context-param> > > <param-name>org.apache.myfaces.STRICT_JSF_2_ALLOW_SLASH_LIBRARY_NAME</param-name> > > <param-value>true</param-value> > > </context-param> > > > DynamicComponent.java: > > @FacesComponent(value="DynamicComponent") > public class DynamicComponent extends UIPanel implements > SystemEventListener{ > public DynamicComponent(){ > UIViewRoot root = getFacesContext().getViewRoot(); > > root.subscribeToViewEvent(PreRenderViewEvent.class, this); > } > > protected FacesContext getFacesContext(){ > return FacesContext.getCurrentInstance(); > } > > @Override > public boolean isListenerForSource(Object source) { > return (source instanceof UIViewRoot); > } > > @Override > public void processEvent(SystemEvent event) { > FacesContext context = getFacesContext(); > debug("in process event=" + event.getClass() + " src " + > event.getSource().getClass()); > > if (!context.isPostback()){ > debug("not a post back " + this.getId()); > > //add a component > UIComponent c1 = createNewComponent(UIInput.COMPONENT_TYPE); > debug("********** created UIInput *********"); > /*************************************************** > * Got a regular component, added that to tree and it gets rendered > */ > if (c1 != null){ > debug(c1); > this.getChildren().add(c1); > } > > //add composite component > > UIComponent c2 = createNewComposite("mylib", "MyCompositeComponent"); > debug("********** created composite component *********"); > /*************************************************** > * Got a composite component, added that to tree and it doesn't get rendered > */ > if (c2 != null){ > debug(c2); > this.getChildren().add(c2); > } > > > }else{ > debug("not a post back"); > } > > } > > private UIComponent createNewComponent(String componentType) { > return > FacesContext.getCurrentInstance().getApplication().createComponent(componentType); > } > > private UIComponent createNewComposite(String lib, String component) { > > Resource resource = > FacesContext.getCurrentInstance().getApplication().getResourceHandler().createResource(component > + ".xhtml", lib); > /*************************************************** > * This is where problem is createComponent using resource returns a > component > * but it doesn;t get rendered when added to tree. > */ > return > FacesContext.getCurrentInstance().getApplication().createComponent(FacesContext.getCurrentInstance(), > resource); > } > > private void debug(UIComponent c){ > > System.out.println(" component is null" + (c==null)); > System.out.println(" component id" + (c.getId())); > System.out.println(" component family" + (c.getFamily())); > System.out.println(" component tostring " + c.toString() ); > > } > > private void debug(String s){ > System.out.println(s); > } > } > > > MyCompositeComponent.xhtml: > > <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" > "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> > > <html xmlns="http://www.w3.org/1999/xhtml" > xmlns:ui="http://java.sun.com/jsf/facelets" > xmlns:cc="http://java.sun.com/jsf/composite" > xmlns:h="http://java.sun.com/jsf/html" > xmlns:f="http://java.sun.com/jsf/core"> > <cc:interface/> > <cc:implementation> > <h:panelGrid columns="2"> > Name : <h:inputText value=""></h:inputText> > Password : <h:inputSecret value=""></h:inputSecret> > </h:panelGrid> > <h:commandButton value="Login" action="login"></h:commandButton> > </cc:implementation> > </html> > > Basically what I'm trying to achieve here is the following: > Test3.xhtml uses the <dynamic/> tag defined in mylib.taglib.xml which > defines the component-type as DynamicComponent. DynamicComponent.java in > turn > adds a stand alone UI component which renders correctly on the page and a > CompositeComponent (MyCompositeComponent.xhtml) which does not render on the > page. > > When I run the above example on MyFaces 2.0.16 I see the following exception > in the logs: > > HtmlComposite E facet UIComponent.COMPOSITE_FACET_NAME not found when > rendering composite component j_id_8:j_id1 > > Is the above a valid use case? I could not find anything specifically in > the JSF 2.0 Spec regarding programatically adding CompositeComponents except > for: > http://docs.oracle.com/javaee/6/api/javax/faces/application/Application.html#createComponent%28javax.faces.context.FacesContext,%20javax.faces.application.Resource%29 > > I've also tried doing the following in the same application with the same > result: > > MyDynamicComponent.xml: > <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" > "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> > > <html xmlns="http://www.w3.org/1999/xhtml" > xmlns:ui="http://java.sun.com/jsf/facelets" > xmlns:cc="http://java.sun.com/jsf/composite" > xmlns:h="http://java.sun.com/jsf/html" > xmlns:f="http://java.sun.com/jsf/core"> > <cc:interface componentType="DynamicComponent"> > </cc:interface> > > <cc:implementation> > </cc:implementation> > </html> > > Test2.xhtml: > <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" > "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> > > <html xmlns="http://www.w3.org/1999/xhtml" > xmlns:ui="http://java.sun.com/jsf/facelets" > xmlns:h="http://java.sun.com/jsf/html" > xmlns:f="http://java.sun.com/jsf/core" > xmlns:test="http://java.sun.com/jsf/composite/mylib"> > > <ui:composition template="/WEB-INF/templates/BasicTemplate.xhtml"> > <ui:define name="content"> > <h2>This test uses dynamic component to create composite component</h2> > <h:form> > > <test:MyDynamicComponent></test:MyDynamicComponent> > </h:form> > </ui:define> > </ui:composition> > </html> > > With the following Warnings and the same exception as above: > > HtmlRenderKit W Unsupported component-family/renderer-type: > javax.faces.Panel/javax.faces.Composite > > UIComponentBa W No Renderer found for component {Component-Path : [Class: > javax.faces.component.UIViewRoot,ViewId: /Test2.xhtml][Class: > javax.faces.component.html.HtmlForm,Id: j_id_8][Class: > com.ibm.ws.jsf.fat.tests.DynamicComponent,Id: j_id_9]} > (component-family=javax.faces.Panel, renderer-type=javax.faces.Composite) > created from: /Test2.xhtml at line 15 and column 35 > > > Any input would be very welcomed! > > Thanks, > > Paul Nicolucci > >
