Building a Complete Website using the Template Toolkit - Menu Components (Page 12 of 15 )
In the layout template in Example 2-20, we delegate the task of generating a menu for the web site to the menu template. Before we look at how the template does this, let’s see an example of the kind of HTML that we would like it to generate.
<table border="0">
<tr>
<td>
<img src="/images/icon.png" width="4" height="4" />
</td>
<td>
<a href="earth.html">Earth</a>
</td>
</tr>
<tr>
<td>
<img src="/images/icon.png" width="4" height="4" />
</td>
<td>
<a href="magrethea.html">Magrethea</a>
</td>
</tr>
</table>
The entire menu is defined as a <table> element, containing one <tr> row for each item, each of which holds two <td> cells, one to display an icon, the other a link to a particular page. Only two items are in this simple example, but already we can see how it gets repetitive very quickly. This suggests that we can modularize the markup into separate template components.
Simple Menu Template
Example 2-23 shows a menu template that defines the outer <table> elements and uses a second template, menuitem, to generate each item.
Example 2-23. lib/menu
<table border="0">
[%
PROCESS menuitem
text = 'Earth'
link = 'earth.html';
PROCESS menuitem
text = 'Magrethea'
link = 'magrethea.html';
%]
</table>
[% BLOCK menuitem %]
<tr>
<td>
<img src="/images/icon.png" width="4" height="4" />
</td>
<td>
<a href="[% link %]">[% text %]</a>
</td>
</tr>
[% END %]
The menuitem template block is defined at the bottom of the menu template, but that doesn’t stop us from using it earlier in the same template, before it is defined.
The menuitem block will remain defined while the menu template is being processed. Any other templates that are called from within the menu template (e.g., by a PROCESS or INCLUDE directive) will also be able to use the menuitem block.
Component Libraries
When a template is loaded using the PROCESS directive, any BLOCK definitions within it will be imported and available for use in the calling template. Templates loaded using the INCLUDE directive keep to themselves and don’t export their BLOCK definitions (or any of their local variables, as described in the earlier discussion of the
INCLUDE directive).
This feature allows you to create single template files that contain libraries of smaller template components, defined using the BLOCK directive. This is illustrated in Example 2-24.
Example 2-24. lib/mylib
[% BLOCK image -%]
<img src="[% src %]" alt="[% alt %]"
width="[% width %]" height="[% height %]" />
[%- END %]
[% BLOCK link -%]
<a href="[% link %]">[% text %]</a>
[%- END %]
[% BLOCK icon;
INCLUDE image
src = '/images/icon.png'
alt = 'dot icon'
width = 4
height = 4 ;
END
-%]
Notice how the icon BLOCK definition is defined within a single directive, and consists of nothing more than a call to the image template component, defined earlier in the same file. This illustrates how easy it is to reuse existing components to quickly adapt them for more specific, or alternate purposes. The BLOCK definitions can be loaded from the mylib template with a PROCESS directive. Then they can be used just like any other template component. Example 2-25 shows a variation of the menu template from Example 2-23 in which the icon and
link components are used to generate the menu items.
Example 2-24. lib/mylib
[% BLOCK image -%]
<img src="[% src %]" alt="[% alt %]"
width="[% width %]" height="[% height %]" />
[%- END %]
[% BLOCK link -%]
<a href="[% link %]">[% text %]</a>
[%- END %]
[% BLOCK icon;
INCLUDE image
src = '/images/icon.png'
alt = 'dot icon'
width = 4
height = 4 ;
END
-%]
Example 2-25. lib/menu2
[% PROCESS mylib %]
<table border="0">
[%
PROCESS menuitem
text = 'Earth'
link = 'earth.html';
PROCESS menuitem
text = 'Magrethea'
link = 'magrethea.html';
%]
</table>
[% BLOCK menuitem %]
<tr>
<td>
[% PROCESS icon %]
</td>
<td>
[% PROCESS link %]
</td>
</tr>
[% END %]
The EXPOSE_BLOCKS option
You can also set an option that allows you to use BLOCK directives without having to first PROCESS the template in which they’re defined. The expose_blocks option for ttree and the corresponding EXPOSE_BLOCKS option for the Template module can be set to make this possible.
For example, by adding the following to the etc/ttree.cfg file:
expose_blocks
we can then access a BLOCK in the mylib template like so:
[% PROCESS mylib/icon %]
The template name, mylib, is followed by the BLOCK name, icon, separated by a / (slash) character. The notation is intentionally identical to how you would specify the icon file in the mylib directory. This is another example of how the Template Toolkit abstracts certain underlying implementation details so that you don’t tie yourself down to one particular way of doing something.
At a later date, for example, you might decide to split the mylib template into sepa rate files, stored in the mylib directory. The same directive will continue to work because the syntax is exactly the same for blocks in files as it is for files in directories:
[% PROCESS mylib/icon %]
This gives you more flexibility in allowing you to change the way you organize your template components, without having to worry about how that might affect the templates that use them.
The FOREACH Directive
The menu component from Example 2-25 can be simplified further by first defining a list of menu items and then iterating over them using the FOREACH directive. Example 2-26 demonstrates this.
Example 2-26. lib/menu3
[% PROCESS mylib %]
[% menu = [
{ text = 'Earth'
link = 'earth.html' }
{ text = 'Magrethea'
link = 'magrethea.html' }
]
%]
<table border="0">
[% FOREACH item IN menu %]
<tr>
<td>
[% PROCESS icon %]
</td>
<td>
[% PROCESS link
text = item.text
link = item.link
%]
</td>
</tr>
[% END %]
</table>
The menu variable is defined as a list of hash arrays, each containing a text
and link item:
[% menu = [
{ text = 'Earth'
link = 'earth.html' }
{ text = 'Magrethea'
link = 'magrethea.html' }
]
%]
The main body of the template defines an HTML <table> element. Within the table, the FOREACH directive iterates through the menu list, setting the item variable to each element in turn.
<table border="0">
[% FOREACH item IN menu %]
<tr>
<td>
[% PROCESS icon %]
</td>
<td>
[% PROCESS link
text = item.text
link = item.link
%]
</td>
</tr>
[% END %]
</table>
The block following the FOREACH directive, up to the corresponding END, can contain text and other directives, even including nested FOREACH blocks. To make the code easier to read, we might prefer to define the menuitem BLOCK , as shown in Example 2-25. This allows us to simplify the FOREACH directive, merging it into a single tag.
The FOREACH block now contains just one directive to PROCESS the menuitem component. The text and link variables are set to the item.text and item.link values, respectively.
<table border="0">
[% FOREACH item IN menu;
PROCESS menuitem
text = item.text
link = item.link;
END
%]
</table>
When the items in a FOREACH list are hash arrays, as they are in Example 2-26, you can omit the name of the item variable:
<table border="0">
[% FOREACH menu;
PROCESS menuitem;
END
%]
</table>
In this case, the values in each hash array will be made available as local variables inside the FOREACH block. So item.text becomes the text variable, and item.link becomes link, but only within the scope of the FOREACH block. This conveniently allows us to process the menuitem template without needing to explicitly derefer ence the item variables.
There’s one more improvement we can make by taking advantage of the Template Toolkit’s side-effect notation. Instead of writing the PROCESS menuitem directive in the FOREACH block all by itself, we can put it before the FOREACH and do away with the semicolons and END keyword:
<table border="0">
[% PROCESS menuitem FOREACH menu %]
</table>
All these enhancements to the menu template are shown in Example 2-27.
Example 2-27. lib/menu4
[% PROCESS mylib %]
[% menu = [
{ text = 'Earth'
link = 'earth.html' }
{ text = 'Magrethea'
link = 'magrethea.html' }
]
%]
<table border="0">
[% PROCESS menuitem FOREACH menu %]
</table>
[% BLOCK menuitem %]
<tr>
<td>
[% PROCESS icon %]
</td>
<td>
[% PROCESS link %]
</td>
</tr>
[% END %]
Defining and Using Complex Data
The variables that we have used so far have mostly been simple scalar variables that contain just one value. The few exceptions include the tantalizing glimpses of the template variable, and the Date plugin in Example 2-22. As we saw in Chapter 1, the Template Toolkit also supports lists and hash arrays for complex data, and allows you to access Perl subroutines and objects.
In this section, we will look more closely at defining and using complex data struc tures, and describe the different Template Toolkit directives for inspecting, presenting, and manipulating them.
 | If you've enjoyed what you've seen here, or to get more information, click on the "Buy the book!" graphic. Pick up a copy today!
Visit the O'Reilly Network http://www.oreillynet.com for more online content. |