Struts in Action: Developing Applications with Tiles, Page 2
11.2 Building a layout template
The first step in building any layout is to identify the component parts. For a classic web page, the parts would be a header, menu, body, and footer. Often a simple sketch can help to bring the layout into focus.
To provide a quick example, we will build a layout for a classic web page with a header, a menu, a body, and footer. Our layout sketch is provided in figure 11.3.
Figure 11.3 The classic master template layout includes a header, body, menu, and footer.
Creating the template page for a layout like the one in figure 11.3 is as easy as it looks:
- Open a new JSP page.
- Import the Tiles taglib.
- Create an HTML table with cells that match the sketch.
- Use a Tiles JSP tag (<tiles:insert>) to name each part of the layout.
Listing 11.1 Markup fragment for our layout page
<%@ taglib uri="/tags/tiles" prefix="tiles" %><TABLE border="0" width="100%" cellspacing="5"><TR> <TD colspan="2"><tiles:insert attribute="header" /></TD></TR><TR> <TD width="140" valign="top"> <tiles:insert attribute="menu"/> </TD> <TD valign="top" align="left"> <tiles:insert attribute="body" /> </TD></TR><TR> <TD colspan="2"> <tiles:insert attribute="footer" /> </TD></TR></TABLE>
In most cases, the body tile will change for each page, but all pages in the same area could share the same header and menu tiles. Meanwhile, all pages on the same site might share a single footer tile. When a new year rolls around and it is time to update the copyright notice to display the new current year on every page, only the one footer tile need be edited.
Our classic layout can be made into a complete, stand-alone template page just by adding the rest of the HTML markup.
In listing 11.2, you'll note that we slipped in a new Tiles tag, <tiles:getAsString name="title"/>. This tag says to return an attribute value as a literal string rather than as a pathname or other command. To do this, Tiles calls the objects standard toString() method. The result is inserted directly into the page at runtime.
Listing 11.2 Classic layout as a complete template page: myLayout.jsp
<%@ taglib uri="/tags/tiles" prefix="tiles" %><HTML> <HEAD> <TITLE><tiles:getAsString name="title"/></TITLE> </HEAD><BODY><TABLE border="0" width="100%" cellspacing="5"><TR> <TD colspan="2"><tiles:insert attribute="header" /></TD></TR><TR> <TD width="140" valign="top"> <tiles:insert attribute='menu'/> </TD> <TD valign="top" align="left"> <tiles:insert attribute='body' /> </TD></TR><TR> <TD colspan="2"> <tiles:insert attribute="footer" /> </TD></TR></TABLE></BODY></HTML>
11.2.1 But what is a tile?The template features offered by the Tiles framework far surpass what the standard Servlet and JSP includes offer. The framework refers to its templates as tiles. This is to help indicate that tiles are more powerful than simple JSP templates. Tiles are building blocks for your presentation layer.Technically, a tile is a rectangular area in a JSP, sometimes referred to as a region. A tile may be assembled from other tiles. Tiles can be built recursively and represented as a tree, as shown in figure 11.4. Each node on the tree is a region. The root node is usually the page. Final nodes, or leaves, contain the page content. Intermediate nodes are usually layouts. The layout nodes are utility tiles that either position a tile within the page or provide background markup for the content tiles.
The tile objects support several important features, including parameters and Definitions.
Figure 11.4 Tiles can be represented as a tree: the page is the root, and the layout tile is a branch (intermediate node), which then includes its own leaves (final nodes).
A tile can accept variable information at runtime in the form of parameters or attributes. This means that tiles are parameterizable. They can accept variable information and act upon themselves accordingly. The tiles parameters are usually called attributes to avoid confusion with request parameters.
Tile attributes are defined when inserting the tile and are visible within the tile only. They aren't visible in subtiles or to a page enclosing the tile. This avoids name conflicts when the same tile is used several times in the same page. Developers can stay focused on making the best use of Tiles without worrying about name contention.
Tile attributes can be strings and other types. See section 11.4 for more about tile attributes.
Taken together, the various attributes passed to a tile create a description of the screen. In practice, many of these descriptions are related and tend to build on one another.
Definitions store a set of attributes so that a screen description becomes a discrete object with its own identity. Declare a base screen Definition and then create other Definitions derived from that base. If the particulars of a base screen change, then all the Definitions extended from that base are also changed. This brings the object-oriented principles of inheritance and encapsulation to your dynamic pages. We cover the Tiles Definition in section 11.4.
Definitions are optional. You can also deploy a tile at any time using a simple JSP tag.
11.2.2 Deploying a Tiles template
The layout template we built in 11.2.1 defined where to position its tiles but not what tiles to use. Those details—and any other particulars—are passed to the layout when it is deployed. The simplest way to do this is to call the layout from another JSP.
Listing 11.3 shows a JavaServer Page being used to pass to a layout what tiles it should use. When hello.jsp is rendered, it will return myLayout.jsp with the content of the specified tiles.
Listing 11.3 Deploying an instance of the classic layout: /pages/hello.jsp
<%@ taglib uri="/tags/tiles" prefix="tiles" %><tiles:insert page="/layouts/myLayout.jsp" flush="true"> <tiles:put name="title" value="Hello World" /> <tiles:put name="header" value="/tiles/header.jsp" /> <tiles:put name="footer" value="/tiles/footer.jsp" /> <tiles:put name="menu" value="/tiles/menu.jsp" /> <tiles:put name="body" value="/tiles/helloBody.jsp" /></tiles:insert>
To use the same layout with a different body, we simply substitute the tags
<tiles:put name="title" value="Hello World" /> <tiles:put name="body" value="/tiles/helloBody.jsp" />
with new particulars, like
<tiles:put name="title" value="Hello Again" /> <tiles:put name="body" value="/tiles/pageTwo.jsp" />
This new page would look much like the original hello.jsp, except with a different title (Hello Again) and a different body tile (the contents of pageTwo.jsp).
You can continue to reuse a layout this way, substituting different attribute values as needed. This passing of parameters makes it possible to use a single base template to lay out every page on a site. If the website layout has to be altered, then only the one base template need be changed.
However, to get the full value of this approach, you have to create at least two JSP files for each new page deployed: one file for the new content and then a second file to insert the template and include the new content in the first file. Later in the chapter, we show you how to use Tiles Definitions with Struts to avoid the overhead of a second file. The body-wrap deployment approach covered in the next section is another way to avoid the second file.
Any tile can be used any number of times in an application. In practice, though, most of the tiles in your application will be content tiles that provide the distinct portion of any given page.
Typically, a page's content tile is used only once in the application. The header, footer, and menu tiles may be used over and over again from page to page. But the multiuse tiles are usually window dressing for each page's singular content tile.
When this is the case, you can simply wrap the content tile with the rest of the screen definition. The trick is to just provide the markup as the value of the put tag. As shown in listing 11.4, be sure to specify type="string" so that Tiles does not mistake it for the path to a page.
Listing 11.4 Deploying a layout using the body-wrap technique
<%@ taglib uri="/tags/tiles" prefix="tiles" %> <tiles:insert page="/layouts/myLayout.jsp" flush="true"> <tiles:put name="title" value="Hello World" /> <tiles:put name="header" value="/tiles/header.jsp" /> <tiles:put name="body" type="string"> <%-- Place the content from /tiles/pageTwo.jsp here --%> </tiles:put> <tiles:put name="footer" value="/tiles/footer.jsp" /> <tiles:put name="menu" value="/tiles/menu.jsp" /></tiles:insert>
This avoids creating an extra tile. A side effect is that it prevents a body tile from being reused on another page. When the body markup does need to be used in more than one place, you would have to refactor the page so that the content is a separate tile again. But for nearly all your pages, the content tile will be used only once.
The body wrap is a very effective approach. The only downside is that the screen definitions are dispersed throughout the site, which can make some global changes more difficult. The Tiles Definitions, described in section 11.3, provide a more centralized approach that works very well with the Struts architecture. But the body-wrap deployment pattern can still be a good choice for smaller applications.
11.2.3 Adding a style sheet
Since tiles are JSP pages, all the usual accouterments are available, including CSSs [W3C, CSS]. Using a style sheet with your tiles is not required but can be helpful.
While full support of CSSs eludes today's browsers, they are still a useful way to define color schemes and some other key attributes. Style sheets help ensure that these niceties remain consistent from tile to tile.
To specify a style sheet, simply insert the usual tag. The <html:base> tag can help resolve relative paths to the style sheets and other assets. Better yet, use the <html:rewrite> tag to render the path for you, and URL-encode it in the bargain. Listing 11.5 shows how to use both tags.
Listing 11.5 Using <html:base> and <html:rewrite>
<%@ taglib uri="/tags/struts-html" prefix="html" %><HTML> <HEAD> <TITLE><tiles:getAsString name="title"/></TITLE> <html:base/> <LINK rel="stylesheet" type="text/css" ref="<html:rewrite page='/assets/styles/global.css'/>"> </HEAD><BODY>
11.2.4 Templates and MVCDynamic templates work especially well within a Model-View-Controller architecture. (See chapter 2 for more about MVC.) Used correctly, dynamic templates cleanly separate markup from content. In practice, the portion of a page with the actual content is often tucked away in the center, surrounded by areas devoted to markup and navigation. Content, markup, and navigation map easily to the roles of Model, View, and Controller.
Often, an MVC template system can be created from an existing application just by using standard refactoring techniques. The base markup for the page can be extracted into a master template. The site header and footer are extracted into their own files; the center square with the actual content goes into its own template file as well. The base template includes them back again. Related technologies, like CSS, can be used to define formatting in the base template and have it apply seamlessly to the others that form the final page.
|DEFINITION||Refactoring is the process of improving software by restructuring its source code for clarity and flexibility and by eliminating redundant or unused code.|
The Tiles package takes this one step further with its Definition feature. Using Definitions, we can streamline the number of physical templates an application needs and move the implementation details into a central JavaServer Page or, better yet, an XML document.