As Gary mentioned the clay tag must not have any other tag except symbol tag in its body.
 
But to solve the templating using clay xml and without clay tag, I have created a util class with the code borrowed from clay component. The class is as follows:
 

public class ClayUtil
{
    /**
     * @return returns the chains catalog for clay commands.
     * @throws Exception
     */
    static protected Catalog getCatalog() throws Exception
    {
        // Look up the "shale" catalog, creating one if necessary
        Catalog catalog = CatalogFactory.getInstance().getCatalog(
                Globals.CLAY_CATALOG_NAME);

        if (catalog == null)
        {
            ConfigParser parser = new ConfigParser();
            URL url = "">                    Globals.CLAY_RESOURCE_NAME);
            if (url == null)
            {
                throw new IllegalArgumentException(Globals.CLAY_RESOURCE_NAME);
            }
            parser.parse(url);
           
            catalog = CatalogFactory.getInstance().getCatalog(
                    Globals.CLAY_CATALOG_NAME);
        }
       
        return catalog;
       
    }
   
    /**
     * @param jsfid the template id.
     * @return returns the component bean associated with the template.
     */
    static protected ComponentBean getRootElement(String jsfid)
    {
        ConfigBean config = ConfigBeanFactory.findConfig (jsfid);
       
        if (config == null)
        {
            throw new NullPointerException("clay config not loaded");
        }
       
        // find the top-level display element associated with the subtree
        ComponentBean b = config.getElement(jsfid);
        if (b == null)
        {
            throw new NullPointerException("clay template with following jsfid not found: " + jsfid);
        }
       
        return b;
    }

    /**
     * @param parent the parent component.
     * @param jsfid the template jsfid.
     * @param symbols the symbols collection used by the template.
     */
    static public UIComponent loadTemplate(UIComponent parent, String jsfid, Map symbols)
    {
        ComponentBean cb = getRootElement(jsfid);
       
        ClayContext clayContext = new ClayContext();
        clayContext.setChild(null);
        clayContext.setDisplayElement(cb);
        clayContext.setJsfid(jsfid);
        clayContext.setFacesContext(FacesContext.getCurrentInstance());
       
        clayContext.setSymbols(symbols);
       
        clayContext.setRootElement(cb);
        clayContext.setParent(parent);
        clayContext.setChildIndex(parent.getChildCount());
       
        Catalog catalog = null;
        try
        {
            catalog = getCatalog();
        } catch (Exception e)
        {
            throw new RuntimeException(e);
        }
       
        Command command = catalog.getCommand(Globals.ADD_COMPONENT_COMMAND_NAME);
       
        try
        {
            command.execute(clayContext);
        } catch (Exception e)
        {
            throw new RuntimeException(e);
        }
       
        return (UIComponent)clayContext.getChild();
    }
}

 
So if the UIComponentTag's createComponentInstance is protected, one can extend the class and override createComponentInstance to use this method to actually load the component from a template.
 
The base class may look like as follows:
 
Class BaseTemplateTag extends UIComponentTag
{

   /**
     * @param symbols  the symbols collection.
     */
    abstract protected void setSymbols(Map symbols);

   protected UIComponent createComponentInstance(FacesContext context, String templateId)

   {
             //get the parent component.
        UIComponent parentComponent = getParentComponent();
        if(parentComponent == null)
        {
            throw new JspException("parent componnet is null");
        }
       
        //initialize symbols
        TreeMap symbols = new TreeMap();
        setSymbols(symbols);
       
        //load template
        return ClayUtil.loadTemplate(parentComponent, getTemplateId(), symbols);
   }
}
 
In the derived classes getComponentType must return the template id and setSymbols must be implemented. Thus the classes derving from this BaseTemplateTag can load templates and also can be nested.
For example:
I have created template definitions for column as follows:
  <component jsfid="mmisColumn" extends="column">
  <attributes>
   <set name="id" value="@fieldColumn"/>
  </attributes>
  <element renderId="1" jsfid="outputText" facetName="header">
   <attributes>
    <set name="id" value="@fieldHeaderText"/>
    <set name="value" value="@headerText"/>
   </attributes>
  </element>
  <element renderId="2" jsfid="outputText">
   <attributes>
    <set name="id" value="@field"/>
    <set name="value" value=" [EMAIL PROTECTED]"/>
   </attributes>
  </element>
 </component>
   
 <component jsfid="mmisSortableColumn" extends="mmisColumn">
  <element renderId="1" jsfid="myfacesCommandSortHeader" facetName="header">
   <attributes>
    <set name="id" value="@fieldHeader"/>
    <set name="columnName" value="@field"/>
   </attributes>
   <element renderId="1" jsfid="outputText">
    <attributes>
     <set name="id" value="@fieldHeaderText"/>
     <set name="value" value="@headerText"/>
    </attributes>
   </element>
  </element>
 </component>
 
 
 similarly  I created a template for dataTable with the look and feel as defined by project style guide.
 
Then I created DataTableTag and columnTag extending BaseTemplateTag. With these in place the jsp code looks like this:
 
              <templates:datatable id="CobList"  bean="OurController">
                <templates:column field="abc"/>
                <templates:column field="def" sortable="true"/>
                <templates:column field="ghi"/>
                <templates:column field="jkl"/>
              </templates:datatable>
 
This reduced a lot of code in the jsp and if there needs a global change to the way datatable or column should look, the change is only in the template.
 
I would appreciate any comments on this.
 
On 6/19/06, Gary VanMatre <[EMAIL PROTECTED] > wrote:
>I agree that facelets can be used. But I have created simple
>util methods that use clay api to load a component or a sub tree.
>I cannot plug this into Myfaces implementation as createComponentInstance
>is private. It can be plugged into sun implementation.
>
>Clay as it is adds a clay component to the tree and while rendering this component
>loads the subtree. Hence one cannot use clay as it is for templating. For example:
>
>The project I am working mostly has panels displaying atleast one list. We use datatable.
>All of the datatable attributes are common except for the bean name. Instead of every
>jsp containing the list, specify all these attributes I created a template using clay for
>datatable with all the attributes set in some cases using symbols.The intention is to
>create a custom tag whose createComponentInstance will create the component from this
>template. Simila rly th e column can be also be templated.

>
>Following are some issues with using clay as it is . Some of them apply to facelets also.
>
>Clay grafts a sub tree into an existing tree. But this is done by adding the clay to main
>tree and the clay  component adds the sub tree as its child.
>
>For example:
>
>When using clay  to load a template, the clay tag is used in jsp.
>
><clay:clay id="abc" jsfid="templatename">
>
></clay:clay>
>
>The execution of this tag adds clay component to the view tree and while rendering, the clay
>component builds the sub tree from the template and adds it as a child.
>
>The tree is as follows:
>
>Viewroot
>|__clay
>|   |__template contents
>|
>|__other page component..
>
>It cannot be used to load a template for a container.
>For example:
>
&gt ;&nbsp ;     When using datatable normally the code looks as follows:

>
><t:datatable attributes…>
>  <h:column>
>    Column contents
>  </h:column>
>  <h:column>
>    Column contents
>  </h:column>
></t:datatable>
>
>The tree is as follows:
>
>Datatable
>
>|__column
>|   |__column contents
>|
>|__column
>
>    |__column contents
>
> As there are many attributes that need to be definedon the datatable, we want to create a
>template. So with clay it will be as follows:
>
><clay:clay id="mydatalist"   jsfid="mytable">
>  <h:column>
>    Column contents
>  </h:column>
>  <h:column>
>    Column conte nts
>  </h:column>
></clay:clay>
>
 
The clay JSP tag should not be used this way.  The only valid nested element
under the clay tag should be the <clay:symbol/> tag.  There is not a tag library
validator to enforce this yet.
 
 
The basic strategy that clay uses is to create a meta data layer that gives more
reuse options.  So, the subtree that clay creates is always from a meta data source
other than nested JSP tags (symbol tag being the exception).
 

The shale-clay-usecases shows several examples but it's not available as a nightly build yet.
We have just switched over to maven 2 builds which means it's about as easy as it gets
to build from the repository.
 
There is actually now another option that we are working out the kinks on.  You can
define an xhtml template using multiple namespaces similar to facelets.  The shale-clay-usecases app
shows an example of this
(http://svn.apache.org/viewvc/struts/shale/trunk/shale-apps/shale-clay-usecases/src/main/webapp/rolodex/jrolodex.html?view=markup )..
 
 
 
>But what happens is the tree will be as follows:
>
>Clay
>|__datatable
>|
>|__column
>|   |__column contents
>|
>|__column
>    |__column contents
>
>
>The template contents become children of clay as well as the components inside
>clay tag body also become its children.
>
> [snippet]

Gary
 

Reply via email to