Advanced HTML::Template: Filters
by Philipp JanertNovember 30, 2006
HTML::Template is a very simple, yet extremely useful module to achieve true separation of presentation and logic when programming CGI scripts. The basic idea is that, rather than having print statements scattered all through your code (the "classic" CGI approach), or mixing logic in with HTML (as in JSP, ASP, and Perl Mason), you maintain two files. One is the actual Perl script containing the business logic and the other one is a template file, containing exclusively presentation layer statements.
The template is straight-up HTML, augmented by a small set of special tags. The templating system replaces these tags by dynamic content. The dynamic content comes from a Perl data structure that you have built in your code.
Here is a minimal template and the corresponding script:
<html>
<head></head>
<body>
<h1><TMPL_VAR NAME=title></h1>
<ul>
<TMPL_LOOP NAME=rows>
<li><TMPL_VAR NAME=item>
</TMPL_LOOP>
</ul>
</body>
</html>
#!/usr/bin/perl -w
use HTML::Template;
# Create the template object
my $tpl = HTML::Template->new( filename => 'tmpl1.tpl' );
# Set the parameter values
$tpl->param( title => "Useful Books" );
$tpl->param( rows => [ { item => "Learning Perl" },
{ item => "Programming Perl" },
{ item => "Perl Cookbook" } ] );
# Print
print "Content-Type: text/html\n\n", $tpl->output;
That's really all there is to it.
As Simple as Possible, but Not Simpler?
One of the nice things about HTML::Template is that doesn't try to do too much. It is a templating system for HTML, period. That keeps it simple and avoids complementary features, which could get in the way.
In this spirit, the module only offers a very, very limited set of tags, and no obvious way to extend this selection. Here is the complete list:
<TMPL_VAR><TMPL_LOOP><TMPL_IF><TMPL_ELSE><TMPL_UNLESS><TMPL_INLCUDE>
All of these tags do pretty much what you might expect: <TMPL_VAR> expands to dynamic text; <TMPL_LOOP> loops over an array; <TMPL_IF>, <TMPL_ELSE>, and <TMPL_UNLESS> provide a mechanism for conditional display; and <TMPL_INLCUDE> allows you to include pieces of other documents (such as a shared page header).
Most of the time, this is precisely what you want. All the facilities are there to control the display, but there is no danger in mixing general-purpose code in with the HTML template.
Yet, sometimes things can become unnecessarily clumsy--in particular, concerning the conditional display of information. A typical example concerns optional information. Suppose that you want to display a list of library books with their due dates, but should display the due date only if the book is currently checked out. (This is a surprisingly common pattern. Think of items with an optional sales price, accounts with an optional bad account status, a flight schedule with optional delay information, etc.)
Sometimes you can simply leave the corresponding variable blank:
<ul>
<TMPL_LOOP NAME=books>
<li><TMPL_VAR NAME=title> <TMPL_VAR NAME=duedate>
</TMPL_LOOP>
</ul>
and use the code:
$tpl->param( books => [ { title => 'Learning Perl', duedate => '' },
{ title => 'Programming Perl', duedate => '29. Feb. 2008' } ] );
This will work. There will be no output for the due date if there is no due date. However, what if you want to have some additional text, in addition to the actual due date? That template might resemble:
<ul>
<TMPL_LOOP NAME=books>
<li><TMPL_VAR NAME=title>
<TMPL_IF NAME=duedate>
Date due: <TMPL_VAR NAME=duedate>
</TMPL_IF>
</TMPL_LOOP>
</ul>
Do this a lot, and you will start looking for a better solution! (The main problem here is not actually the additional typing, but the fact that the template itself is becoming increasingly unwieldy and its structure harder to follow.)
One solution is to put the additional text in code:
$tpl->param( books => [ { title => 'Learning Perl', duedate => '' },
{ title => 'Programming Perl', duedate => 'Date due: 29. Feb. 2008' } ] );
However, this is clumsy: it breaks the separation of presentation and behavior, as there is now strictly presentational material such as "Date due: " in the Perl code, and it makes the parameter less generally useful. If you want the plain date somewhere else in the template, it now comes bundled with a string that you may not want.
Wouldn't it be nice if you could bundle the entire optional part of each list entry ("encapsulate it," if you will) and simply call it by name? To put it in other words, what if you had the ability to define a custom tag?
|
Related Reading Perl Hacks |
Pages: 1, 2 |


