14.1 Understanding Templates

Traditional GUI toolkits such as VisualWorks Smalltalk or Java Swing all provide some type of layout manager that dictates how content should be displayed within the frame or window. With typical web sites, the layout can undergo many changes, both small and large, over its lifetime. Using layouts and layout managers can help to encapsulate the physical areas of the pages within an application so that they can be altered with minimal impact to the rest of the application. Unfortunately, the JSP technology does not natively provide any direct support for layouts or layout managers. This is why the template-based approach was invented. The concept of templates is not a new one—it has been around for many years, in one form or another.

To understand how templates can actually simplify a web site's layout, let's compare it with an approach that uses the JSP include mechanism. The current index.jsp page of the Storefront application is shown in Example 14-1.

Example 14-1. The index.jsp page for the Storefront application
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
 
<html:html>
 <head>
 <title><bean:message key="global.title"/></title>
 <html:base/>
 <script language=javascript src="include/scripts.js"></script>
 <link rel="stylesheet" href="stylesheets/format_win_nav_main.css" type="text/css">
 </head>
 
 <body topmargin="0" leftmargin="0" bgcolor="#FFFFFF">
 
 <!-- Header information -->
 <%@ include file="include/head.inc"%>
 
 <!-- Menu bar -->
 <%@ include file="include/menubar.inc"%>
 
 <!--- Include the special offer -->
 <%@ include file="include/mainoffer.inc"%>    
 
 <!-- Featured items header row -->
 <table width="645" cellpadding="0" cellspacing="0" border="0">
  <tr>   
   <td width="21">
   <html:img height="1" alt="" page="/images/spacer.gif" width="1" border="0"/>
   </td>
   <td width="534">
   <html:img page="/images/week_picks.gif" altKey="label.featuredproducts"/>
   </td>
   <td width="1" bgcolor="#9E9EFF">
   <html:img height="1" alt="" page="/images/spacer.gif" width="1" border="0"/>
   </td>
   <td width="1"  bgcolor="#9E9EFF">
   <html:img height="1" alt="" page="/images/spacer.gif" width="1" border="0"/>
   </td>
   <td width="90" bgcolor="#9E9EFF" align="right">
   <html:img height="1" alt="" page="/images/spacer.gif" width="90" border="0"/>
   </td>
  </tr>
  <tr>
   <td>
   <html:img height="1" alt="" page="/images/spacer.gif" width="21" border="0"/>
   </td>
   <td colspan="4" bgcolor="#9E9EFF">
   <html:img height="1" alt="" page="/images/spacer.gif" width="1" border="0"/>
   </td>
  </tr>
 </table>
 
 <html:img height="10" alt="" page="/images/spacer.gif" width="1" border="0"/><br>
 
 <!--- Include the featured items -->
 <%@ include file="include/featureditems.inc"%>
 
 <!--- Include the copyright statement -->
 <%@ include file="include/copyright.inc"%>
 </body>
</html:html>

Although the main page uses the JSP include directive, the layout is mixed with content in the page. For example, notice that the page specifies explicitly that the head.inc include file comes first, then the menubar.inc file, the mainoffer.inc file, and so on, right down to the copyright.inc include at the bottom of the page. For every page that we want to have this particular layout, we need to add the same statements in the same order. If a customer wants the menu along the left side instead of across the top, every page will have to be changed.

The Storefront application uses the JSP include mechanism rather than a straight JSP approach. Although the include mechanism is a step in the right direction because it does reduce redundancy (imagine if we included the copyright content in every page!), it's still less efficient than a template-based approach.

Static Versus Dynamic Content

With JSP, there are two different kinds of content to include: static and dynamic. The include directive shown here:

<%@ include file="include/copyright.inc" %>

includes the source of the target page at translation/compile time. Therefore, it's not possible to include runtime content using the include directive. The JSP include directive treats a resource as a static object, and the context of the resource is included literally in the page.

In direct contrast, the include action shown here:

<jsp:include page="include/copyright.inc"/>

handles the resource as a dynamic object. The request is sent to the resource, and the result of the processing is included. Templates use a dynamic approach so that runtime expressions can be evaluated and included.

14.1.1 What Is a Template?

A template is a JSP page that uses a JSP custom tag library to describe the layout of a page. The template acts as a definition for what the pages of an application will look like, without specifying the content. The content is inserted into the template page at runtime. One or more pages may use the same template.

The purpose of a template is to get a consistent look and feel within an application without having to hardcode it for every page. It makes sense that most of the pages will use the same template; however, it's not uncommon to have a different look and feel for a few pages within an application and therefore to require more than one template.

Example 14-2 illustrates a template for the Storefront application.

Example 14-2. A basic template for the Storefront application
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html"%>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean"%>
<%@ taglib uri="/WEB-INF/tiles.tld" prefix="tiles"%>
 
<html:html>
 <head>
  <title><bean:message key="global.title"/></title>
  <html:base/>
 </head>
 <body topmargin="0" leftmargin="0" bgcolor="#FFFFFF">
 
  <!-- Header page information -->
  <tiles:insert attribute="header"/>
 
  <!-- Menu bar -->
  <tiles:insert attribute="menubar"/>  
 
  <!-- Main body information -->
  <tiles:insert attribute="body-content"/>
 
  <!-- Copyright information -->
  <tiles:insert attribute="copyright"/>
 </body>
</html:html>

Not many new concepts are introduced in the template file in Example 14-2. The first thing that you should notice is that we are using Struts custom tag libraries. The fact that we are using the Tiles tag library as well as the HTML and Bean libraries shouldn't be too shocking; the Tiles tag library is just like any other. We'll talk in detail about the Tiles tag library later in this chapter.

The rest of the page is a mixture of HTML layout tags. You should notice that there's no content included, only insert tags where content will be inserted at runtime. You should already be familiar with the Struts tags shown here, so we won't say anything about them. The insert tag performs a role similar to that of the JSP include directive. It's basically saying that somewhere there's a variable called header, for instance, and that the attribute value of "header" should be passed to the insert tag, and the content that is produced should be inserted right here. The same thing goes for the menubar, body-content, and copyright inserts. We'll explain shortly how the "real" content is substituted for these attributes at runtime.

Notice that this layout is very similar to the one shown in Example 14-1. The only difference is that instead of explicitly including the mainoffer and featureditem includes, as Example 14-1 does, the template file includes a body-content section. This allows us to reuse the template for any page that has this generic format. Once we figure out how to supply the page-specific body content, we can reuse this template over and over again. This one file can then control the layout of multiple pages. If we need to modify the layout of the site, this is the only file we need to change—that's the real power of using a template-based approach.

The last piece of the puzzle is how the header, menubar, body-content, and copyright sections are put together to form the rendered output. The important point to remember is that the JSP page shown in Example 14-2 is the template. You still need JSP pages that supply page-specific content used by the template. For example, if we rewrite the index.jsp page from Example 14-1 using the template from Example 14-2, it will look like the one in Example 14-3.

Example 14-3. The index.jsp page for the Storefront application using a template
<%@ taglib uri="/WEB-INF/tiles.tld" prefix="tiles" %>
 
<tiles:insert page="/layouts/storefrontDefaultLayout.jsp" flush="true">  
  <tiles:put name="header" value="/common/header.jsp" />     
  <tiles:put name="menubar" value="/common/menubar.jsp" />     
  <tiles:put name="body-content" value="/index-body.jsp" />     
  <tiles:put name="copyright" value="/common/copyright.jsp" />     
</tiles:insert>

The first thing to notice in Example 14-3 is that the Tiles tag library is included at the top. Every page (or tile) that needs to use the Tiles tag library must include it with this line:

<%@ taglib uri="/WEB-INF/tiles.tld" prefix="tiles" %>

Two tags from the Tiles library are used in Example 14-3: insert and put. (The complete set of Tiles tags and their associated attributes are discussed later in this chapter.) You already saw the insert tag in Example 14-2, but it's performing a slightly different function in Example 14-3. Two attributes are being supplied to the insert tag: page and flush. The page attribute informs the tag that this JSP page is using a particular template (or layout, in the Tiles world—layouts also are discussed later in this chapter). We are calling the template from Example 14-2 storefrontDefaultLayout.jsp. The flush attribute informs the controller to flush the page output stream before inserting content into the result page.

The put tag in Example 14-3 answers a question that we asked in the previous section: how does the page-specific content get supplied to the template? As you can see, the attributes for the put tag in this example are name and value. If you compare the values of the different name attributes, you'll see that they match up to the ones that the template file in Example 14-2 expects. When the index.jsp page from Example 14-3 is executed, the template file is processed and dynamically passed the header.jsp, menubar.jsp, index-tile.jsp, and copyright.jsp files from the put tags:

<tiles:insert page="/layouts/storefrontDefaultLayout.jsp" flush="true">  
  <tiles:put name="header" value="/common/header.jsp" />     
  <tiles:put name="menubar" value="/common/menubar.jsp" />     
  <tiles:put name="body-content" value="/index-body.jsp" />     
  <tiles:put name="copyright" value="/common/copyright.jsp" />     
</tiles:insert>

At runtime, the values of the put tags are dynamically substituted into the template file and processed. The resulting output is what gets displayed to the client.

To wrap up the discussion of templates, here is another page that uses the same template from Example 14-2 but supplies a different body-content. Example 14-4 shows the itemdetail.jsp page.

Example 14-4. The itemdetail.jsp page for the Storefront application
<%@ taglib uri="/WEB-INF/tiles.tld" prefix="tiles" %>
 
<tiles:insert page="../layouts/storefrontDefaultLayout.jsp" flush="true">  
  <tiles:put name="header" value="../common/header.jsp"/>
  <tiles:put name="menubar" value="../common/menubar.jsp"/>
  <tiles:put name="body-content" value="../catalog/itemdetail-body.jsp"/>  
  <tiles:put name="copyright" value="../common/copyright.jsp"/>       
</tiles:insert>

The only difference between the index.jsp page in Example 14-3 and the itemdetail.jsp page in Example 14-4 is the content supplied by the body-content attribute.

If you are still not convinced of the value of using templates, notice that the index.jsp and itemdetail.jsp pages in Example 14-3 and Example 14-4 do not specify anything about how the content should be laid out. They both reference the storefrontDefaultLayout.jsp file, which has sole responsibility for displaying the content in a prescribed format. If we want to change the layout of the site, we have to modify only the storefrontDefaultLayout.jsp file.