Creating composition components in JSF 2.0

A great feature of Facelets consists in composition components (available starting with JSF 2.0). For a better understanding, we will start with a traditional application, and we will compare it with an application that uses composition components. At the end, you will love composition components.

Getting ready

We have developed this recipe with NetBeans 6.8, JSF 2.0, and GlassFish v3. The JSF 2.0 classes were obtained from the NetBeans JSF 2.0 bundled library.

How to do it...

Let's suppose that we have a list of books, characterized by title, author, and price, and we need to display this list in a table and provide the sorting (ascending/descending) action for each category (see next screenshot):

How to do it...

Focusing on this view (not on functionality or backing beans), we will probably write a JSF page like the following (this is the traditional approach):

<!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:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title> Composition components in JSF 2.0</title>
<style type="text/css">
.header { text-align: left;
letter-spacing:5px;
color:#000099
}
.odd { background-color: yellow }
.even { background-color: orange }
</style>
</h:head>
<f:view>
<h:form>
<h:dataTable id="booksId" value="#{booksStore.books}" var="bk"
rowClasses="odd, even" headerClass="header">
<!-- book title -->
<h:column>
<f:facet name="header">
<h:panelGroup>
<h:outputText value="Book Title" />
<f:verbatim>[</f:verbatim>
<!-- Ascending link -->
<h:commandLink action="#{booksStore.sortBooks}">
<h:outputText id="booktitleascid" value="ascending" />
<f:param name="by" value="title"/>
<f:param name="order" value="ascending"/>
</h:commandLink>
<h:outputText value="," />
<!-- Descending link -->
<h:commandLink action="#{booksStore.sortBooks}">
<h:outputText id="booktitledescid" value="descending" />
<f:param name="by" value="title"/>
<f:param name="order" value="descending"/>
</h:commandLink>
<f:verbatim>]</f:verbatim>
</h:panelGroup>
</f:facet>
<h:outputText value="#{bk.title}" />
</h:column>
<!-- book author -->
<h:column>
<f:facet name="header">
<h:panelGroup>
<h:outputText value="Book Author" />
<f:verbatim>[</f:verbatim>
<!-- Ascending link -->
<h:commandLink action="#{booksStore.sortBooks}">
<h:outputText id="bookauthorascid" value="ascending" />
<f:param name="by" value="author"/>
<f:param name="order" value="ascending"/>
</h:commandLink>
<h:outputText value="," />
<!-- Descending link -->
<h:commandLink action="#{booksStore.sortBooks}">
<h:outputText id="bookauthordescid" value="descending" />
<f:param name="by" value="author"/>
<f:param name="order" value="descending"/>
</h:commandLink>
<f:verbatim>]</f:verbatim>
</h:panelGroup>
</f:facet>
<h:outputText value="#{bk.author}" />
</h:column>
<!-- book price -->
<h:column>
<f:facet name="header">
<h:panelGroup>
<h:outputText value="Book Price" />
<f:verbatim>[</f:verbatim>
<!-- Ascending link -->
<h:commandLink action="#{booksStore.sortBooks}">
<h:outputText id="bookpriceascid" value="ascending" />
<f:param name="by" value="price"/>
<f:param name="order" value="ascending"/>
</h:commandLink>
<h:outputText value="," />
<!-- Descending link -->
<h:commandLink action="#{booksStore.sortBooks}">
<h:outputText id="bookpricedescid" value="descending" />
<f:param name="by" value="price"/>
<f:param name="order" value="descending"/>
</h:commandLink>
<f:verbatim>]</f:verbatim>
</h:panelGroup>
</f:facet>
<h:outputText value="#{bk.price}" />
</h:column>
</h:dataTable>
</h:form>
</f:view>
</html>

In addition, we have two backing beans as follows:

A Book.java backing bean that maps the book characteristics:

package bean;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
@ManagedBean
@SessionScoped
public class Book {
private String title;
private String author;
private String price;
public Book(String title, String author, String price) {
this.title = title;
this.author = author;
this.price = price;
}
public Book() {
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}

A BooksStore.java backing bean that defines a list of Book instances and defines a method for sorting the books by title, author, or price is shown next:

package bean;
import java.util.ArrayList;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
@ManagedBean
@SessionScoped
public class BooksStore {
private List books = new ArrayList();
public BooksStore() {
books.add(new Book("Learning Website Development with Django",
"Ayman Hourieh", "€26.34"));
books.add(new Book("Building Websites with Joomla! 1.5",
"Hagen Graf", "€29.74"));
books.add(new Book("ASP.NET 3.5 Application Architecture and
Design", "Vivek Thakur", "€30.99"));
books.add(new Book("Drupal 6 Themes", "Ric Shreves", "€26.34"));
books.add(new Book("WordPress Theme Design",
"Tessa Blakeley Silver", "€26.34"));
}
public List getBooks() {
return books;
}
public void setBooks(List books) {
this.books = books;
}
public void sortBooks() {
FacesContext facesContext = FacesContext.getCurrentInstance();
HttpServletRequest httpServletRequest = (HttpServletRequest)
facesContext.getExternalContext().getRequest();
String by = httpServletRequest.getParameter("by");
String order = httpServletRequest.getParameter("order");
System.out.println("The books should be order " + order +
" by " + by + "!");
//ordering books
}
}

Obviously, the redundancy of the JSF view is annoying and very primitive. We can fix this by defining a composition component that can be invoked instead of repeating code (the backing beans remain unchanged). The composition component can be created by following a few steps. To start with, we define the composition component page and place it under the /WEB-INF folder:

<!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>
<h:column>
<f:facet name="header">
<h:panelGroup>
<f:verbatim>Book-</f:verbatim>
<h:outputText value="${attr}" />
<f:verbatim>[</f:verbatim>
<!-- Ascending link -->
<h:commandLink action="#{compbean.sortBooks}">
<h:outputText value="ascending" />
<f:param name="by" value="${attr}"/>
<f:param name="order" value="ascending"/>
</h:commandLink>
<h:outputText value="," />
<!-- Descending link -->
<h:commandLink action="#{compbean.sortBooks}">
<h:outputText value="descending" />
<f:param name="by" value="${attr}"/>
<f:param name="order" value="descending"/>
</h:commandLink>
<f:verbatim>]</f:verbatim>
</h:panelGroup>
</f:facet>
<h:outputText value="${book[attr]}" />
</h:column>
</ui:composition>
</html>

Next, we define a tag library file to map the tag name to the tag source or, in other words, to map the name of the composition component with the composition component source page. In addition, it defines the namespace used to access the tag. This is an XML file that looks like this:

<?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.my.facelets.component.com/jsf</namespace>
<tag>
<tag-name>tableColumn</tag-name>
<source>mycomp.xhtml</source>
</tag>
</facelet-taglib>

Further, we declare the tag library in the web.xml descriptor—you need to indicate the path to the tag library as follows:

<context-param>
<param-name>javax.faces.FACELETS_LIBRARIES</param-name>
<param-value>/WEB-INF/facelets/tags/taglibmycomp.xml</param-value>
</context-param>

Finally, we import the corresponding namespace and invoke the composition component. The client page for our composition component is listed 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:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:x="http://www.my.facelets.component.com/jsf">
<h:head>
<title>Composition components in JSF 2.0</title>
<style type="text/css">
.header { text-align: left;
letter-spacing:5px;
color:#000099
}
.odd { background-color: yellow }
.even { background-color: orange }
</style>
</h:head>
<f:view>
<h:form>
<h:dataTable id="booksId" value="#{booksStore.books}" var="bk"
rowClasses="odd, even" headerClass="header">
<x:tableColumn book="${bk}" attr="title"
compbean="${booksStore}" />
<x:tableColumn book="${bk}" attr="author"
compbean="${booksStore}" />
<x:tableColumn book="${bk}" attr="price"
compbean="${booksStore}" />
</h:dataTable>
</h:form>
</f:view>
</html>

As you can see, now the code is simpler, cleaner, and more optimal.

Regarding the values passed by the client page to the composition component, we need to notice the following (is very important to keep this in mind and to adapt it to your applications):

  • When the composition component is invoked we pass three attributes, representing a Book instance (bk), a constant string, and a BooksStore instance (booksStore)
  • The Book is passed using the book="${bk}" construction, and it is used as the ${book[attr]} construction, where attr can be title, author, or price depending on attr attribute value
  • The constant is passed as attr="title", "author", and"price" and it is used as ${attr}
  • The BooksStore instance is passed as compbean="${booksStore}", and it is used as #{compbean.sortBooks}

How it works...

The composition component acts as a reusable component. The client page calls the composition component as many times as it wants and each time it customizes it by passing it different values. As you just saw, Facelets provides support for passing different kinds of values, and, as you will see in the next two recipes, we can go even deeper on this line. As we can customize the composition component aspect, behavior, and so on, we can create a application without multiplying "islands" of code all over the application. Put this in correlation with templating and you will obtain great code!

See also

The code bundled with this book contains a complete example of this recipe. The project can be opened with NetBeans 6.8 and it is named: Create_composition_components_in_JSF20.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset