Facelets (JavaServer Faces View Definition Framework) is a view technology that focuses on building JSF component trees. Facelets includes many features such as:
- Works with JSF 1.1 and JSF 1.2, including Sun's RI and Apache MyFaces.
- Fast Templating/Decorators for Components and Pages
- The ability to specify UIComponent trees in separate files (UICompositions)
- Line/Tag/Attribute precise Error Reporting
- Full EL support, including Functions
- Plugable Decorators to really make designer's job easy
- Works with any RenderKit
What you need before we get started
- ShoppingCartFaceletsPart6 sample application. - This is an Eclipse archive file that contains two Eclipse projects. Please import these projects into your Eclipse environment.
- You will need the previous third party libraries that were mentioned and used in earlier parts to this tutorial
- The following libraries need to be added to your existing Libs project
What we will cover
- Facelets template ability
- XHTML and JSPX
- Shopping Cart application changes
Level
- Beginner
Template
I am primarily using Facelets for its template ability. We have three JSP pages and each of them share common parts of the page, such as the header where the banner goes. Our site is pretty simple so there is not much to it. In the previous tutorial I used a JSP include tag to pull the header section into every JSP page. In this tutorial I am going to show you how you can define a template using Facelets and allow your pages to define which template to use.
XHTML and JSPX
One of the first things you may have noticed is that all the JSP files have been replaced by JSPX files. By default, Facelets expects pages to use the *.xhtml file extension. XHTML (The Extensible Hypertext Markup Language) is a markup language that has the same depth of expression as HTML, but also conforms to XML syntax. XHTML is a stricter and cleaner version of HTML. Unfortunately we can not use .xhtml files because Eclipse does not recognize files of this type to be used with JSF as a result Eclipse does not offer tag completion or validation for the libraries registered in the XML namespaces.
There is a work around to this and that is using the .jspx file extension as opposed to the .xhtml file extension. The tag completion in Eclipse is built to support JSPX, and all that Facelets requires is a valid XML page. Facelets is none the wiser that the XML being used is rooted as JSP. The declarations appearing in the XML namespaces now provide Eclipse with what it needs to offer tag library support while informing Facelets which components to load. The only requirement is to ensure that the corresponding TLD file for each tag library is located on the classpath. Even though Facelets does not require TLDs, Eclipse needs them to load the tag libraries definitions, as it does not understand the tag configurations used by Facelets. I copied the jsf-ui.tld tag library to the tlds folder in our WebContent/WEB-INF directory.
Shopping Cart
These are the changes I made to the Shopping Cart application in order to use Facelets:
web.xml
Set the default suffix parameter to .jspx:
<context-param> <param-name>javax.faces.DEFAULT_SUFFIX</param-name> <param-value>.jspx</param-value> </context-param>
faces-config.xml
I overwrote the default view handler implementation with Facelets own view handler implementation. The view handler class is called during the Restore View and Render Response phases of the request processing lifecycle.
<application> <view-handler>com.sun.facelets.FaceletViewHandler</view-handler> </application>
Facelets Template
I created a facelets template called template.jspx. Every other page will inherit from. It contains the layout of our page as well as the header section:
Notice the facelets ui:insert tag. This tag declares a named content element to be defined by another Facelet. You will see in the pages to come that it is used with the ui:define tag to pass values between Facelets.
<?xml version="1.0" encoding="ISO-8859-1" ?> <jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" version="2.0"> <ui:composition> <html xmlns="http://www.w3.org/1999/xhtml"> <f:loadBundle var="msg" basename="Application-Resources" /> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" /> <title>#{msg.title}</title> <link rel="stylesheet" href="#{facesContext.externalContext.request.contextPath}/styles/shopping.css" type="text/css" /> </head> <body> <div class="header"><h:outputText id="headerOutTxt" value="#{msg.title}" styleClass="headerOutputText" /></div> <p /> <ui:insert name="body"></ui:insert> </body> </html> </ui:composition> </jsp:root>
items.jspx
There are 2 things I would like to highlight in this page, they are:
- ui:compostion – this tag wraps content to be included in another Facelet. I use this tag to specify which template should be applied to this page
- ui:define – this tag is used to define the name of the content. It is used in conjunction with the insert tag specified in our template
<?xml version="1.0" encoding="ISO-8859-1" ?> <jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" version="2.0"> <ui:composition template="/template.jspx"> <ui:define name="body"> <h:outputText id="headingOutTxt" value="#{msg.items_txt_heading}" styleClass="headingOutputText" /> <h:form id="itemListFrm"> <h:dataTable id="itemsTbl" var="items" value="#{pc_Items.items}" binding="#{pc_Items.itemsTable}" styleClass="dataTable" columnClasses="dataTableCol1" rowClasses="dataTableRow1" headerClass="dataTableHeader"> <h:column id="nameCol"> <f:facet name="header"> <h:outputText id="nameHeaderOutTxt" value="#{msg.items_txt_name_col_header}" /> </f:facet> <h:outputText id="nameOutTxt" value="#{items.name}" /> </h:column> <h:column id="descriptionCol"> <f:facet name="header"> <h:outputText id="descriptionHeaderOutTxt" value="#{msg.items_txt_description_col_header}" /> </f:facet> <h:outputText id="descriptionOutTxt" value="#{items.description}" /> </h:column> <h:column id="priceCol"> <f:facet name="header"> <h:outputText id="priceHeaderOutTxt" value="#{msg.items_txt_price_col_header}" /> </f:facet> <h:outputText id="priceOutTxt" value="#{items.price}"> <f:convertNumber pattern="#{msg.currency_pattern}" /> </h:outputText> </h:column> <h:column id="addActionCol"> <h:commandLink id="buyLnk" value="#{msg.items_lnk_buy}" action="#{pc_Items.addItemToBasket}" /> </h:column> </h:dataTable> <p /> <h:dataTable id="basketTbl" var="basketItem" value="#{shoppingViewHelper.basket.items}" binding="#{pc_Items.basketTable}"> <h:column id="basketCol"> <f:facet name="header"> <h:outputText id="basketTblHeaderOutTxt" value="#{msg.items_txt_basket_header}" /> </f:facet> <h:outputText id="basketItemOutTxt" value="#{basketItem.name}" /> </h:column> <h:column id="removeActionCol"> <h:commandLink id="removeLnk" value="#{msg.items_lnk_remove}" action="#{pc_Items.removeItemFromBasket}" /> </h:column> </h:dataTable> <p /> <h:commandButton id="checkoutBtn" value="#{msg.btn_checkout}" action="#{pc_Items.checkout}" /> </h:form> </ui:define> </ui:composition> </jsp:root>
The same principles apply to the checkout.jspx and the payment-processed.jspx files.