Thanks for the information Leonardo! So calling the below method: http://docs.oracle.com/javaee/6/api/javax/faces/application/Application.html#createComponent%28javax.faces.context.FacesContext,%20javax.faces.application.Resource%29
Will just create the composite component but then trying to add this
returned component to a page is not possible currently in JSF 2.0 for the
reasons
you listed in your previous answer. Am I understanding this correctly?
The best practice is to leave the dynamic component tree manipulation to
facelet components like <c:if ...> or <ui:include src="#{}">, because
those components are integrated with the Partial State Saving algorithm.
Just let facelets handle
component tree creation/manipulation.
If I'm incorrect let me know :)
Regards,
Paul Nicolucci
From: Leonardo Uribe <[email protected]>
To: MyFaces Development <[email protected]>,
Date: 01/17/2013 06:44 PM
Subject: Re: Programmatically Adding Composite Component
Problem/Questions
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
>
>
<<inline: graycol.gif>>
