Creating Composition Components in JSF 2.0

0
86
7 min read

(For more resources on JSF, see here.)

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):

Creating Composition Components in JSF 2.0

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

>

<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


>

<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>

LEAVE A REPLY

Please enter your comment!
Please enter your name here