Level: Introductory Richard Hightower (rhightower@arc-mind.com), Developer, ArcMind Inc.
21 Feb 2006 Trying to combine JSF and JSP is like trying to shoehorn a foot into a glove: it's possible, but it's really just a stopgap measure until something better comes along. In this article, JSF enthusiast Rick Hightower introduces you to what he likes best about Facelets: easy HTML-style templating and reusable composition components. While working on a Java™Server Faces (JSF) project recently, I had the
pleasure of using Facelets for the first time. What I most liked about
Facelets was that it let me create reusable composition components.
Being able to take a page (like a JSP) and turn it into a component has
been a real boon to my JSF development ever since. My conclusion?
If you're not using Facelets, you're not getting the most you can out of JSF.
The mismatch between JSF and JavaServer Pages technology is a serious
problem in JSF development. The issue is how to integrate JSP's dynamic
content into JSF's component-based model. JSP is singularly focused on
generating dynamic output, whereas JSF requires JSP to coordinate building
a component model. The disjunct occurs because that task is beyond the original intention of JSP.
Most JSF developers simply learn to navigate such problems on an
ad-hoc basis, but that's kind of like duct-taping a pillow to a hammer
so it won't hurt coming down on your head. Facelets is a much
more comprehensive solution: a templating language that is geared
toward the JSF component model.
Facelets has several compelling features:
- Templating (like Tiles)
- Composition components
- Custom logic tags
- Expression functions
- Designer-friendly page development
- Creating component libraries
 |
JEE WARTAC blog
Author Rick Hightower takes his discussion of Facelets one step further in JEE WARTAC, a blog that focuses on topics related to the tools he uses in his daily development, including JSF, Facelets, Hibernate, Spring, Tomahawk, MyFaces, RDBMS systems, and iBatis.
|
|
These features are far more related -- and integrated -- than you
might think. In this article, I discuss the first two: templating and
composition components. I use a Web application example based on one I
developed for my JSF for nonbelievers series, updating it to use
Facelets views rather than Tiles. You should download the example code before reading further.
You'll also need to install Facelets if you want
to follow along with the discussion. .
Overview of Facelets
One of the biggest mistakes you can make is to assume that Facelets
is merely a replacement for Tiles. Facelets is much more than that: it's a new way of thinking about JSF.
 |
Choose your markup
Although most developers use Facelets with XHTML, the framework is
actually markup agnostic: it's compatible with XUL (XULFaces) and Kito Mann
has used it to render the RSS feeds for JSF Central.
|
|
JSP is a templating language that produces a servlet. The body of the
JSP becomes the equivalent of a servlet's doGet() and doPost() methods
(that is, it becomes the jspService() method). The
JSF custom tags (such as f:view and h:form) are just calls to the JSF components to
render themselves in their current state. The life cycle of the JSF
component model is independent from the life cycle of the JSP-produced
servlet. That independence is where the confusion comes in.
Unlike JSP, Facelets is a templating language built from the ground
up with the JSF component life cycle in mind. With Facelets, you produce
templates that build a component tree, not a servlet. This allows for
greater reuse because you can compose components out of a composition of
other components.
Facelets obviates the need to write custom tags to use JSF
components. Facelets uses JSF custom components natively. Very little
special coding is needed to bridge JSF and Facelets: all you have to do
is declare the JSF components in a Facelet tag library file. You can use
JSF components directly within the Facelets templating language without
any additional development.
The Facelets template framework
Facelets is similar to Tapestry (see Resources) in that it provides a template framework geared toward component construction. For those of us who come from a JSP background, however, Facelets seems a lot friendlier than Tapestry. It lets you work with JSTL-style tags and a JSTL/JSF/JSP-style expression language that you're already familiar with. The greatly reduced learning curve means you can jump into development much more quickly.
 |
Facelets and Tapestry
Facelets is similar enough to Tapestry to draw comparison. In fact,
Tapestry was well ahead of its time when it first came out, and Facelets
does draw on some of its ideas. But it would be a mistake to simply
think of Facelets as a JSF version of Tapestry. The two technologies are
different. To learn more about Tapestry, see Brett McLaughlin's two-part
series, "In tune
with Tapestry."
|
|
Facelets allows you to define component assemblies that can be included directly into a page or can easily be added to a Facelet tag library.
It's actually amazing how quickly you can define custom tags (composition components and tags similar to JSP custom tags) in Facelets.
With these component assemblies, Facelets also allows you to define site templates (and smaller templates). This is very similar to working with Tiles
but minus the definition files. You can also use Facelets inside of a custom JSF component because the Facelets API provides an interface that is easy to integrate with.
From Tiles to Facelets
As I mentioned, the example Web application I use here is based on
one I created for my JSF for
nonbelievers series. It's a create, read, update, and delete (CRUD)
listing that manages inventory for an online CD store. It includes a
form that lets the users enter a new CD into the system and a list of
radio buttons that lets them select a music category. When the user
selects a category, you fire off some JavaScript to post the form
immediately back to the server. The application also includes a CD
listing from which the the user can sort CDs by title or artist.
Figure 1 is a UML diagram of the application classes:
Figure 1. Class diagram for the online CD store example
Figure 2 gives you a look at the store's CD listing page:
Figure 2. The online CD store's listing page
Whereas the original application got its view support from Tiles,
I'll build this one using Facelets. I'll start by replacing the Tiles
support in the example with Facelets, then jump into writing composition
components. Before I get into any of that, though, you'll need to have
Facelets installed.
Installing Facelets
The steps to install Facelets are easy to follow. Note that I'm assuming you've already downloaded and installed the example application.
- Download the Facelets distribution and unzip it.
- Copy the jsf-facelets.jar into your WEB-INF/lib directory (when the application is deployed, it must end up in WEB-INF/lib directory).
- Add the Facelet init parameter(s) to the web.xml file.
- Add the FaceletViewHandler to the faces-config.xml file.
Steps 1 and 2 are basic. I'll cover the other two in detail.
Adding init parameters
This step assumes you already have a working JSF application (such as the online CD store example) installed and you are
editing an existing web.xml page by adding the following parameter:
<context-param>
<param-name>javax.faces.DEFAULT_SUFFIX</param-name>
<param-value>.xhtml</param-value>
</context-param>
|
This tells JSF to assume a prefix of xhtml, which the Facelet’s renderer can interpret.
Facelets has many parameters, so see Resources for a
complete listing. If you're having problems with the example, refer to
the DEVELOPMENT init parameter, which is good for
debugging. Setting the REFRESH_PERIOD parameter to
low is also helpful during development.
Adding the FaceletViewHandler
For Facelets templates to take effect, you need to tell JSF
about the Facelets view handler. A JSF ViewHandler is a plug-in that handles the Render
Response and Restore View phases of the JSF request-processing life cycle
for different response-generation technologies, including Facelets.
(Anyone who believes JSF isn't extensible is misinformed!) You plug
Facelets into JSF by adding the following view handler to
faces-config.xml:
<application>
<locale-config>
<default-locale>en</default-locale>
</locale-config>
<view-handler>com.sun.facelets.FaceletViewHandler</view-handler>
</application>
|
Templating with Facelets
I'll walk you through the Facelets templating framework first, because it's relatively easy to understand. The steps to create and use a Facelets template are as follows:
- Create a layout.xhtml page.
- Import the use of Facelets by defining the Facelet's namespace.
- Use
ui:insert tag to define logical areas of the page.
- Use plain text and
ui:include tags to define reasonable defaults.
I'll walk through these steps one by one, using the online CD store's
Listing page as my layout example.
Step 1. Create a layout.xhtml page
The layout.xhtml page is just a normal XHTML text file that uses the following doctype declaration:
<!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">
</html>
|
No further detail required!
Step 2. Define the Facelets' namespace
To use Facelets tags for templating, you need to import them using the XML namespace as follows:
<!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">
...
|
Notice the definition of the ui namespace.
Step 3. Use ui:insert tag to define logical areas of the page
Next, you define logical areas of your layout like title, header, navigation, content, and more. Here's an example of how you could define a title:
<!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">
<head>
<title><ui:insert name="title">Default title</ui:insert></title>
<link rel="stylesheet" type="text/css" href="./css/main.css"/>
</head>
...
|
Notice the use of the ui:insert tag to
define the title's logical area. The text "Default title" inside of the
ui:insert element defines what the text is if the user of the template does not pass the title. You could also
write the above as follows:
Step 4. Use plain text and ui:includes to define defaults
You can pass more than plain text as default. For example, study the following code fragment from layout.xhtml:
<div id="header">
<ui:insert name="header">
<ui:include src="header.xhtml"/>
</ui:insert>
</div>
|
Here I've used the ui:insert tag to define
the logical area and the ui:include tag to insert the default. By default,
the page using the layout uses the contents of header.xhtml as the
header text, but because the header is a logical area defined by ui:insert, the page using this template can also
pass a different header. For an application with a front end (e.g., a
catalog with a shopping cart) and backend administration (such as for
adding a new product), the backend site could have different links in
the header or navigation. The ui:include tag
makes it easy to swap out the default header with a new header.
Listing 1 shows the complete code for the example application's
Listing page, list.xhtml:
Listing 1. The complete list.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">
<head>
<title><ui:insert name="title">Default title</ui:insert></title>
<link rel="stylesheet" type="text/css" href="./css/main.css"/>
</head>
<body>
<div id="header">
<ui:insert name="header">
<ui:include src="header.xhtml"/>
</ui:insert>
</div>
<div id="left">
<ui:insert name="navigation" >
<ui:include src="http://www.ibm.com/developerworks/java/library/j-facelets/navigation.xhtml"/>
</ui:insert>
</div>
<div id="center">
<br />
<span class="titleText"> <ui:insert name="title" /> </span>
<hr />
<ui:insert name="content">
<div>
<ui:include src="http://www.ibm.com/developerworks/java/library/j-facelets/content.xhtml"/>
</div>
</ui:insert>
</div>
<div id="right">
<ui:insert name="news">
<ui:include src="http://www.ibm.com/developerworks/java/library/j-facelets/news.xhtml"/>
</ui:insert>
</div>
<div id="footer">
<ui:insert name="footer">
<ui:include src="http://www.ibm.com/developerworks/java/library/j-facelets/footer.xhtml"/>
</ui:insert>
</div>
</body>
</html>
|
Now that you know how to define a layout, I'll show you how to use it!
Using a Facelets template
To invoke a template, you use the ui:composition tag. To pass arguments to the
template, you use the ui:define tags, which
are subelements of the ui:composition tag.
In Listing 2, I've called the layout page for the online CD store
example:
Listing 2. Calling the layout page
<!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">
<ui:composition template="/WEB-INF/layout/layout.xhtml">
<ui:define name="title">CD form</ui:define>
<ui:define name="content">
<!-- use the form tag to set up this form -->
<h:form id="cdForm">
...
...
...
</h:form>
</ui:define>
</ui:composition>
</html>
|
Notice the inclusion of the following namespaces in the above call:
- xmlns:h="http://java.sun.com/jsf/html"
- xmlns:f="http://java.sun.com/jsf/core"
With Facelets, you don't rely on the JSF tag libraries, so to use the core and HTML JSF components, you must import them through the above namespaces.
The use of the html tag might seem odd.
After all, the layout page shown in Listing 2 is invoking a template
that already has an html tag; so does that
mean you'll get two html tags? As it happens,
everything that is on the outside of the ui:composition tag is ignored, so all the html tag does is make the HTML fragment viewable by
HTML editors. It doesn't impact on run-time behavior.
Location is
everything
When the page invokes the layout template, it
just needs to specify the location of the template, as shown here:
<ui:composition template="/WEB-INF/layout/layout.xhtml">
|
This tag invokes the template shown in Listing 1, so all I need to do is
pass the parameters to the template. Then, inside the composition
flag, I can pass simple text like the title:
<ui:define name="title">CD form</ui:define>
|
or an entire component tree:
<ui:define name="content">
<!-- use the form tag to setup this form -->
<h:form id="cdForm">
...
...
...
</h:form>
</ui:define>
|
Notice that of the many logical areas I could have defined and passed, the cdForm.xhtml only passes two: content and title.
 |
Tiles versus Facelets
Three examples ship with this article: the first uses Tiles, the second uses Facelets, and the third uses composition components. I've included
the Tiles example so that you can compare and contrast different approaches to templating in the two frameworks. Give it a shot and see what you think!
|
|
Composition components
If you used Facelets only to define and use templates, you might be a bit disappointed. Although Facelets templating is full-featured and rich, it doesn't have as many features as a framework like Tiles, which is good for defining defaults, hierarchies of related templates, and such.
Templating isn't where Facelets really shines, though: Facelets puts its best foot forward with composition components. (Interestingly,
composition components also lend some benefits to Facelets templating;
for example, you can leave out f:verbatim
tags and miscellaneous h:outputText tags in
Facelets because everything is treated as a component in a component
tree. More on this later.)
For the remainder of the article, I'll focus on the steps involved in creating and using composition components. Before I do that, though,
let's make sure you have a clear picture of what makes these
handy little code snips so great.
Breaking the DRY principle
Have you ever written code that looks like the fragment shown in Listing 3?
Listing 3. Life before composition components
<h:dataTable id="items" value="#{CDManagerBean.cds}" var="cd"
rowClasses="oddRow, evenRow" headerClass="tableHeader">
<!-- Title -->
<h:column>
<f:facet name="header">
<h:panelGroup>
<h:outputText value="Title" />
<f:verbatim>[</f:verbatim>
<h:commandLink styleClass="smallLink"
action="#{CDManagerBean.sort}">
<h:outputText id="ascTitle" value="asc" />
<f:param name="by" value="title"/>
<f:param name="order" value="asc"/>
</h:commandLink>
<h:outputText value="," />
<!-- Sort descending -->
<h:commandLink styleClass="smallLink"
action="#{CDManagerBean.sort}">
<h:outputText id="decTitle" value="dec" />
<f:param name="by" value="title"/>
<f:param name="order" value="dec"/>
</h:commandLink>
<f:verbatim>]</f:verbatim>
</h:panelGroup>
</f:facet>
<h:outputText value="#{cd.title}" />
</h:column>
<!-- Artist -->
<h:column>
<f:facet name="header">
<h:panelGroup>
<h:outputText value="Artist" />
<f:verbatim>[</f:verbatim>
<h:commandLink styleClass="smallLink"
action="#{CDManagerBean.sort}">
<h:outputText id="ascArtist" value="asc" />
<f:param name="by" value="artist"/>
<f:param name="order" value="asc"/>
</h:commandLink>
<h:outputText value="," />
<!-- Sort descending -->
<h:commandLink styleClass="smallLink"
action="#{CDManagerBean.sort}">
<h:outputText id="decArtist" value="dec" />
<f:param name="by" value="artist"/>
<f:param name="order" value="dec"/>
</h:commandLink>
<f:verbatim>]</f:verbatim>
</h:panelGroup>
</f:facet>
<h:outputText value="#{cd.artist}" />
</h:column>
|
This code from listing.xhtml generates column headers and
sort-ascending and sort-descending links for the example application's
Listing page. Notice how I had to duplicate the code in multiple places
to output multiple columns. (You'll also note in the above example that
I switch between ${..} and #{..}; this might be confusing, but they do the same thing!)
All that repeated code to render the Title and Artist columns
breaks the DRY principle -- that is, don't repeat yourself. And
what's wrong with that, you say? Well, imagine if you had an average of
5 columns in your listings and 20 different listings in the application.
Using the approach in Listing 3, you would have to repeat the same 35 lines of
code 100 times for a combined total of 3,500 lines of code! Maintaining
all that code would be a pain, but what if you ever decided to change
the presentation of the listing or (gasp!) add a generic way to
do list filtering? Far, far too much work.
Now compare Listing 3 with this one:
Listing 4. A new way to create fields
<h:dataTable id="items" value="#{CDManagerBean.cds}" var="cd"
rowClasses="oddRow, evenRow" headerClass="tableHeader">
<a:column entity="${cd}" fieldName="title" backingBean="${CDManagerBean}"/>
<a:column entity="${cd}" fieldName="artist" backingBean="${CDManagerBean}"/>
|
Looks like I've replaced 70 or more lines of code with just 4! As
you've guessed, the a:column is a composition
component. It's easy to define a component like this one in Facelets, as
you can see in Listing 5:
Listing 5. column.xhtml renders a column with a sort link
<!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:z="http://www.qualcomm.com/jsf/core"
xmlns:c="http://java.sun.com/jstl/core"
xmlns:fn="http://java.sun.com/jsp/jstl/functions">
THIS TEXT WILL BE REMOVED
<ui:composition>
<!-- The label attribute is optional. Generate it if it is missing. -->
<c:if test="${empty label}">
<c:set var="label" value="${fieldName}" />
</c:if>
<!-- The sort attribute is optional. Set it to true if it is missing. -->
<c:if test="${empty sort}">
<c:set var="sort" value="${true}" />
</c:if>
<h:column>
<f:facet name="header">
<h:panelGroup>
${label}
<c:if test="${sort}">
[
<h:commandLink styleClass="smallLink"
action="#{backingBean.sort}">
<h:outputText value="asc" />
<f:param name="by"
value="${fieldName}"/>
<f:param name="order" value="asc"/>
</h:commandLink>
,
<!-- Sort descending -->
<h:commandLink styleClass="smallLink"
action="#{backingBean.sort}">
<h:outputText value="asc" />
<f:param name="by"
value="${fieldName}"/>
<f:param name="order" value="dec"/>
</h:commandLink>
]
</c:if>
</h:panelGroup>
</f:facet>
<!-- Display the field name -->
<h:outputText value="${entity[fieldName]}"/>
</h:column>
</ui:composition>
THIS TEXT WILL BE REMOVED AS WELL
</html>
|
The fine points
Before I get into more advanced examples, I want to draw your attention to a few things. First, notice how I referenced the value binding in a generic way in Listing 5:
<h:outputText value="${entity[fieldName]}"/>
|
Second, when I invoke this composition component, I'll pass the entity and fieldName as attributes, as shown here:
<a:column entity="${cd}" fieldName="title" backingBean="${CDManagerBean}"/>
|
The EL spec used by Facelets lets you refer to
fields using the dot (.) notation or the
less-used Map notation. For example, ${entity[fieldName]} would equate to CDManager.title if invoked as above. Notice also
that I didn't need the f:verbatim tags or
ancillary h:outputText. This will be true of
any Facelets page you write. Facelets knows about the JSF component tree,
and its sole purpose in life is to build that component tree. This is
another advantage of Facelets over using JSP and Tiles.
Once I've written it, I can use the column.xhtml composition
component in many other places. As a general rule: If you're breaking
the DRY principle, think about using composition components instead.
Creating a component
You've had a quick look at composition components with the column.xhtml
example. Now let's walk through the process of creating one step-by-step. Here
are the steps to create a composition component:
- Create a Facelets tag library.
- Declare the tag library in web.xml.
- Import the tagfile using namespace.
Step 1. Create a Facelets tagfile
A tagfile is a file that follows the facelet_taglib_1_0.dtd. It is similar in concept to a TLD file in JSP. Listing 6 is an example tag library file:
Listing 6. A tag library file -- arcmind.taglib.xml
<?xml version="1.0"?>
<!DOCTYPE facelet-taglib PUBLIC
"-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN"
"facelet-taglib_1_0.dtd">
<facelet-taglib>
<namespace>http://www.arc-mind.com/jsf</namespace>
<tag>
<tag-name>field</tag-name>
<source>field.xhtml</source>
</tag>
<tag>
<tag-name>column</tag-name>
<source>column.xhtml</source>
</tag>
<tag>
<tag-name>columnCommand</tag-name>
<source>columnCommand.xhtml</source>
</tag>
</facelet-taglib>
|
The arcmind.taglib.xml file declares three tags: field, column (you've
already seen that one!), and columnCommand.
All you have to do is specify the name of the tag using tag-name and the location of the implementation
file. The implementation file name is relative. You'll find all this
code, including the DTD, in the WEB-INF\facelets\tags file of the
example Web application.
Be sure to notice the namespace element that is declared
before the tag element above: you'll need it later to use this tag
library from another Facelets page.
Step 2. Declare the tag library in web.xml
Having a tag library is nice, but for it to be useful, you have to
tell Facelets that it exists. You do this with the facelets.LIBRARIES init parameter in the web.xml
file, as shown here:
<context-param>
<param-name>facelets.LIBRARIES</param-name>
<param-value>
/WEB-INF/facelets/tags/arcmind.taglib.xml
</param-value>
</context-param>
|
Passing facelets.LIBRARIES as a semicolon-delimited list lets you define as many tagfiles as you like.
Step 3. Import the tagfile using namespace
Once you've created your tagfile and defined it in a Facelets tag library, you're ready to use it. You use a tagfile by declaring it as an XML namespace, as shown here:
<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:a="http://www.arc-mind.com/jsf">
...
...
<a:column entity="${cd}" fieldName="title"
backingBean="${CDManagerBean}"/>
<a:column entity="${cd}" fieldName="artist"
backingBean="${CDManagerBean}"/>
<a:column entity="${cd}" fieldName="price"
backingBean="${CDManagerBean}" sort="${false}"/>
<a:columnCommand label="Edit" action="editCD"
backingBean="${CDManagerBean}"/>
|
Notice the namespace defined as follows:
xmlns:a="http://www.arc-mind.com/jsf"
|
The namespace value is the same as the namespace element I
declared in the tag library back in Step 1.
Advanced tricks and tips
That just about covers the basics of composition components. You can
use what I've shown you so far to create reusable components. In my own
use of Facelets, I've discovered some little tricks for making
composition components even more useful, and in some cases working
around small problems. For example, consider the following code fragment
from the cdForm.xhtml template:
Listing 7. A fragment from cdForm.xhtml
<h:form id="cdForm">
<h:inputHidden id="cdid" value="#{CDManagerBean.cd.id}" />
<h:panelGrid id="formGrid" columns="3" rowClasses="row1, row2">
<!-- Title -->
<h:outputLabel id="titleLabel" for="title" styleClass="label"
value="Title" />
<h:inputText id="title" value="#{CDManagerBean.cd.title}"
required="true" />
<h:message id="titleMessage" for="title" styleClass="errorText"/>
<!-- Artist -->
<h:outputLabel id="artistLabel" for="artist" styleClass="label"
value="Artist" />
<h:inputText id="artist" value="#{CDManagerBean.cd.artist}"
required="true" />
<h:message id="titleMessage" for="artist"
styleClass="errorText"/>
<!-- Price -->
<h:outputLabel id="priceLabel" for="price" styleClass="label" value="Price" />
<h:inputText id="price" value="#{CDManagerBean.cd.price}"
required="true">
<f:validateDoubleRange minimum="15.0" maximum="100.0" />
</h:inputText>
<h:message id="priceMessage" for="price" styleClass="errorText"/>
|
The above page is similar in concept to the one shown Listing 3 in that it leaves room for some Facelets love
and a field composition component to get rid of duplicate code. Based on
this code, it should be easy to create a composition component for
displaying fields, but there's one little snag. Do you see it? Look at
the price field of the form: it includes a validator.
Now, how do you pass a validator to a composition component?
Passing subelements
Here's a dirty little secret about Facelets: A composition component is basically a type of template. As such, you can pass a template
argument using the ui:define tag that goes with a particular ui:insert, or you can pass the body as a default ui:insert.
Listing 8 is just such an implementation of the field component (field.xhtml):
Listing 8. field.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:z="http://www.qualcomm.com/jsf/core"
xmlns:c="http://java.sun.com/jstl/core"
xmlns:fn="http://java.sun.com/jsp/jstl/functions"
xmlns:t="http://myfaces.apache.org/tomahawk">
THIS TEXT WILL BE REMOVED
<ui:composition>
<!-- The label is optional.
Generate it if it is missing. -->
<c:if test="${empty label}">
<c:set var="label" value="${fieldName}" />
</c:if>
<!-- The required attribute is optional,
initialize it to true if not found. -->
<c:if test="${empty required}">
<c:set var="required" value="true" />
</c:if>
<h:outputLabel id="${fieldName}Label"
value="${label}" for="#{fieldName}" />
<h:inputText id="#{fieldName}" value="#{entity[fieldName]}"
required="${required}">
<ui:insert />
</h:inputText>
<!-- Display any error message that are found -->
<h:message id="${fieldName}Message"
style="color: red; text-decoration: overline"
for="#{fieldName}" />
</ui:composition>
THIS TEXT WILL BE REMOVED AS WELL
</html>
|
By now, the workaround in Listing 8 should be more or less what you expected.
Notice the use of the unnamed ui:insert tag
inside of h:inputText. Once you've got it,
you can use this composition component as shown in Listing 9:
Listing 9. Field tag composition component
<!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:a="http://www.arc-mind.com/jsf">
...
<h:form id="cdForm">
<!-- Title, Artist, Price -->
<a:field fieldName="title" entity="#{CDManagerBean.cd}" />
<a:field fieldName="artist" entity="#{CDManagerBean.cd}" />
<a:field fieldName="price" entity="#{CDManagerBean.cd}" >
<f:validateDoubleRange minimum="15.0" maximum="100.0" />
</a:field>
...
|
The field tag for price gets passed the validator as an anonymous insert. Because the other fields don't define a body, the anonymous insert introduces nothing as the default.
Passing actions
At times you may want to pass an action binding to create elements
like toolbars and navigation lists. The problem is that with the
standard expression language you can't, but there's a workaround! In the
same way that you can reference fields from an object, you can reference
methods in an object. So, to create a component that creates an action
binding, you could do the following (from the columnCommand.xhtml):
<h:commandLink id="#{action}" value="#{label}"
action="#{backingBean[action]}"/>
|
Study the value of the action attribute. Notice that I've accessed the method in the same way that I earlier referenced a field from the entity. I can
invoke this component using the following syntax:
<a:columnCommand label="Edit" action="editCD"
backingBean="${CDManagerBean}"/>
|
This call binds the editCD() method from
the CDManagerBean to the link when invoked.
Listing 10 shows the complete listing for columnCommand.xhtml:
Listing 10. columnCommand.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:z="http://www.qualcomm.com/jsf/core"
xmlns:c="http://java.sun.com/jstl/core">
THIS TEXT WILL BE REMOVED
<ui:composition>
<!-- The label is optional. Generate it if it is missing. -->
<c:if test="${empty label}">
<c:set var="label" value="${action}" />
</c:if>
<h:column>
<f:facet name="header">
<h:panelGroup>
<h:outputText value="#{label}" />
</h:panelGroup>
</f:facet>
<h:commandLink id="#{action}" value="#{label}"
action="#{backingBean[action]}"/>
</h:column>
</ui:composition>
THIS TEXT WILL BE REMOVED AS WELL
</html>
|
Downsides of Facelets
I've clearly showed the benefits of using Facelets, namely component
composition and a template framework whose lingua franca is
components, not Servlets output. But there are some downsides to
adopting Facelets. For one thing, IDE support for Facelets is
minimal. Only one Eclipse IDE implementation support Facelets at all (a
commercial one; see Resources), and it doesn't
seem to support code completion.
There's also no IDE support for debugging Facelets (that is, setting a
break point and such). For debugging, you need to read the Facelets
manual, turn on its JDK 1.4-style logging, and set up its
init parameters accordingly for development.
On the upside, I found working with the Facelets API to be very
natural and intuitive. The debugging was a bit arcane at first, but
livable in the end. The demo applications that come with the Facelets
distribution do not have examples of custom tags or functions, but the
core project code does, so use that as a guide.
If you use a new JSF component library, you have to have a Facelets tag library file that exposes that library. Some tag libraries exist for major
component libraries like Oracle’s and Tomahawk, but even those need tweaking. I had to tweak the Tomahawk tag library to get the Tomahawk calendar component
in my application. Granted it is somewhat easy to write tag library files that export components, but it's yet another hassle. If you want to use a new custom
component library, you have to write a tag library file.
Facelets seems to only work with MyFaces 1.1.1 and Sun's 1.2 JSF
reference implementation because of problems in other implementations (Sun's
JSF RI 1.2 is not officially out yet). You can't use Facelets with the
1.1 RI. You can't use Facelets with IBM's implementation, although you
can use MyFaces with IBM WebSphere. (If you use the latest version of
Facelets, you have to use the nightly build of MyFaces 1.1.2, which isn't out yet.)
Also note that the underlying mechanics of MyFaces 1.1 and
the JSF RI 1.2 differ. Despite that, Facelets attempts to
accommodate both implementations in their current forms (nightlines of
MyFaces 1.1.2 and JSF RI 1.2), which seems to account for the bulk of the time
spent recently on Facelets. It will be nice when the two conform and solidify a bit more, requiring less time to make Facelets work the same in both environments so more time can be spent improving Facelets.
In conclusion
Even with some shortcomings, I strongly recommend that you download
Facelets and start using it as quickly as possible. Facelets is the
future of JSF, or ought to be, and using it can keep you DRY in any JSF
storm. If you can't use it on your current project, keep it in mind for
your next one.
I've used Facelets to create an entire library of composition
components, custom Facelet tags, and functions for an Internal CRUD
framework. I've discovered a lot more tricks and techniques for building
composition components (for example, a field tag that auto generates a
checkbox, calendar component, or text field based on the type of the
value binding that is bound to the component) than I could cover in an
introductory article like this one. Instead, I've focused on the basics
of getting you up and running with composition components. With what
you've learned here, you can create some amazing components
using just a minimal amount of custom functions and custom Facelets
tags.
Acknowledgment
Special thanks Jacob Hookom, creator of Facelets, for his review and input on this article, and props to Athenz for his careful and insightful editing.
Downloads | Description | Name | Size | Download method |
|---|
| Facelets source code | j-facelets_code.zip | 267KB | HTTP |
|---|
| Facelets source code with jars and wars | j-faceletsjarsandwars.zip | 47MB | HTTP |
|---|
Resources Learn
- "JSF for nonbelievers: Clearing the FUD about JSF" (Richard Hightower, developerWorks, February 2005): A four-part series that defends and demystifies JSF programming. See
Part 2 for the original online CD store example used here.
- "In tune with Tapestry" (Brett McLaughlin, developerWorks, January 2006): An excellent two-part introduction to Tapestry.
- "Inside Facelets Part 1: An Introduction" (Jacob Hookom, JSF Central, August 2005): Thoughts on Facelets from its creator.
- Facelets documentation: See a complete listing of Facelets parameters.
- Combining Spring, JSF, and Hibernate: Author Rick Hightower's musings on combining Spring, JSF and Hibernate.
- Another Sleepless Night in Tucson:
Author Rick Hightower's blog, where he thinks aloud about JSF (not to mention wine and cooking) till the wee hours of the morning.
-
The Java technology zone: Hundreds of articles about every aspect of Java programming.
Get products and technologies
Discuss
About the author  | |  | Rick Hightower
serves as chief technology officer for ArcMind Inc, a training company the
specializes in JSF, Spring and Hibernate. He is coauthor of the popular
book Java Tools for Extreme Programming, about applying extreme
programming to J2EE development, and coauthor of Professional
Struts. |
Original link: http://www.ibm.com/developerwork...
|