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.
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