Kiss My App

Monday, May 16, 2005

Facelets: Templating

Pick your poison. Currently Facelets offers 3 different ways to modularize and decorate your pages/components.

  • Tag Files

  • Decorators

  • Includes


Preface

Facelets is founded on the idea of compositions. This means that a UIComponent tree can be defined in multiple Facelet pages and executed in part or whole. Some other frameworks, like Tapestry, may define a single component within a page, Facelets instead marks the root of a branch of child components-- a composition if you will.

Text before will be removed.
<ui:composition>
#{dynamic.text}
<h:inputText id="myText" value="#{foo.bar}"/>
</ui:composition>
Text after will be removed.

This means you can have an easily viewable HTML document (body/css/js) and all, but only use a portion of the page to define the reusable composition.


Templates

Compositions have a single attribute, "template", that can be used the same way that Struts Tiles does. Lets modify the composition example above for templates:


Text before will be removed.
<ui:composition template="/templates/menu.xhtml">
<ui:define name="title">
#{dynamic.text}
</ui:define>
<ui:define name="body">
<h:inputText id="myText" value="#{foo.bar}"/>
</ui:define>
</ui:composition>
Text after will be removed.

Now for the template--

Template Text before will be removed.
<ui:composition>
This Text will show up
<ui:insert name="title">
Default Title Text
</ui:insert>
This Text will also show up
<ui:insert name="body">
Default Body Text
</ui:insert>
So will this text
</ui:composition>
Template Text after will be removed.

When the Facelet is 'applied' and the tree is built, the template will be weaved in, using the default text/components if the definitions aren't passed. This functionality is the foundation of Facelet templating and has proven to be quite fast.


Includes

This is probably the most familiar method of modularizing content. This allows you to include another Facelet into the UIComponent tree.


<span id="leftNav">
<ui:include src="/WEB-INF/siteNav.xhtml"/>
</span>

The navigation that the include is pointing at could be an XML document with namespaces, or an XHTML page with namespaces that uses the 'ui:composition' tag as described above with templates.


Includes can also have child 'ui:param' tags that work the same as they do with JSP with a name and value attribute. These name/value pairs will be bound and passed to the included Facelet when the UIComponent tree is built:


<span id="leftNav">
<ui:include src="/WEB-INF/siteNav.xhtml">
<ui:param name="user" value="#{currentUser}"/>
<ui:param name="page" value="home"/>
</ui:include>
</span>


Decorators

I previously wrote about 'templates' with compositions. Decorators behave exactly the same way, except that you are inlining the template into the page:


Text before will show up
<ui:decorate template="/templates/menu.xhtml">
<ui:define name="title">
#{dynamic.text}
</ui:define>
<ui:define name="body">
<h:inputText id="myText" value="#{foo.bar}"/>
</ui:define>
</ui:decorate>
Text after will show up

This has practical uses where it doesn't make sense to abstract out a composition into a separate document, but would still like to take advantage of templating.


Tag Files

Tag files are no different than any other page. This means that your tag file can be as simple as something like this:


<!-- this is the whole file! -->
<composition xmlns="http://java.sun.com/jsf/facelet">
#{user.login} on page #{page}
</composition>

You can note that I'm using the same param's that were defined in the 'include' example to prove a point. The include with params has the same behavior defining a tag:

<my:echo user="#{currentUser}" page="home"/>

You will get some performance gain by defining tags instead of using an include because the attributes are wired at compile time, where an include must fetch its child params on each execution.


At this point, you have to wonder how you go about specifying or binding files to tags. Tags must be defined in a Facelet tag library, such as:


<facelet-taglib>
<namespace>http://www.mycompany.com/jsf</namespace>
<tag>
<tag-name>echo</tag-name>
<source>tags/echo.xml</source>
</tag>
</facelet-taglib>

As a side note, tag libraries can either be placed in the META-INF directory of your jar (same as a faces-config.xml), or specified in your application's web.xml.


That's it for now, more to come soon!

12 Comments:

Post a Comment

<< Home