Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask
 
PerlMonks  

Reusable template components with HTML::Template

by dws (Chancellor)
on Aug 04, 2004 at 01:18 UTC ( [id://379870]=perlmeditation: print w/replies, xml ) Need Help??

When you're using HTML::Template and want to put a table into a page, you have two options: You can embed loop logic in the template, or you can inject a table constructed elsewhere into the template. If you favor reuse, you're going to lean towards injection. But that doesn't mean you can't use templates for both parts. Here's a variation that does both, in a reusable way.

A simple embedded table looks like

... stuff ... <table> <TMPL_LOOP rows> <tr> <TMPL_LOOP columns> <td><TMPL_VAR ESCAPE=HTML value></td> </TMPL_LOOP> </tr> </TMPL_LOOP> </table> ... more stuff ...

and the code to expand it looks like

my $template = HTML::Template->new(rows => ...); print $template->output;

You can isolate part of the above into a separate template, and pull it in to the main template via

... stuff ... <TMPL_INCLUDE NAME="table.tmpl"> ... more stuff ...

This expands the included template in-line, with no visible difference to the calling program, though you do get the benefit of having a reusable table template. But you can do better, as we'll see below.

When injecting a table, the template looks like

... stuff ... <TMPL_VAR ESCAPE=0 table> ... more stuff ...

You're then on the hook for providing a chunk of HTML to supply as the template parameter table. You can construct the HTML from code, from a heredoc, by using CGI.pm tag support, or by some other means. Whichever way you go, you'll end up doing something like

my $template = HTML::Template->new(filename => 'bigpage.tmpl'); my $tableHTML = tableHTML(...); $template->param(table => $tableHTML); print $template->output;

A possibly non-obvious "other means" is to produce the table HTML by using a separate HTML::Template instance that uses its own template for the table part. This looks like

sub tableHTML { my %table_data = @_; my $template = HTML::Template->new(filename => 'table.tmpl'); $template->param(%table_data); return $template->output; }

You now have the basis for a reusable component that you can pull out any time you need to embed tables in a template. Variations include passing a query into the component, which then extracts data from some data source. This technique also extends nicely to other types of components.

Replies are listed 'Best First'.
Re: Reusable template components with HTML::Template
by Aristotle (Chancellor) on Aug 04, 2004 at 01:39 UTC

    Or, instead of reinventing this functionality on top of a restrictive templating engine, you use one with a more powerful language such as the Template Toolkit, where you get reusability simply by defining the table template in a [% BLOCK foo %][% END %] and referring to it by [% INCLUDE foo %]. (You can pass parameters, as well. You could put the block in another file. And those are just some of the available options.)

    Don't get me wrong, I used to be a fan of the restrictive approach. But at some point you realize that the templating engine really can't separate application logic from presentation logic for you, and that using a restrictive templating language just makes you jump through unnecessary hoops. I also find that if the templating language is not powerful enough, then presentation logic seeps back into the application — just like you just demonstrated.

    Makeshifts last the longest.

      Or, ... you use one with a more powerful language such as the Template Toolkit

      Template Toolkit is a fine piece of work, but using it has drawbacks in some contexts. Non-programmers have a difficult time coping with it*. It raises a barrier to understanding, and hence a barrier to the willingness of designers to edit templates. And the risk of mal-edits by non programmers is higher. The technique of isolating components helps.

      <TMPL_VAR ESCAPE=0 stuff>
      is fairly easy for a non-programmer to work around. Templates used by components are harder to deal with, but designers spend their time at the page level, at least in my experience.

      The general problem is shared by JSP, ASP, and other schemes.


      *Amended to say that this has been my experience. YMMV.

        I have to say, although I have had success with HTML::Template and continue to use it, I totally disagree with your criticisms of Template Toolkit. I have used it with groups of HTML coders who have no programming experience beyond basic cut-and-paste JavaScript. They had absolutely no problems understanding it. In fact, they soon started asking me to show them some of the fancier features that I had left out, thinking they would be overwhelmed.

        I hear this criticism frequently and I think it comes from people who have never tried using TT with designers. The TT syntax is nearly identical to HTML::Template for all common constructs, and the ability to pass parameters to a template makes the kind of reuse being discussed here a whole lot easier.

Re: Reusable template components with HTML::Template
by FoxtrotUniform (Prior) on Aug 04, 2004 at 01:31 UTC
    A possibly non-obvious "other means" is to produce the table HTML by using a separate HTML::Template instance that uses its own template for the table part. This looks like
    sub tableHTML { my %table_data = @_; my $template = HTML::Template->new(filename => 'table.tmpl'); $template->param(%table_data); return $template->output; }
    You now have the basis for a reusable component that you can pull out any time you need to embed tables in a template. Variations include passing a query into the component, which then extracts data from some data source. This technique also extends nicely to other types of components.

    One thing that concerns me about this approach is that you now have to worry about managing a template widget library. Either you copy table.tmpl into every project directory that needs it, or you introduce some scheme for looking these widgets up every time they're referenced -- and doing version control ala "DLL Hell". (There may be a third way that escapes me at the moment; I suppose you could symlink to table.tmpl every time you need it, but that seems to me like the worst of both worlds.)

    This is hardly an insurmountable problem, but I don't think it's trivial, either. (Maybe I'm wrong; if so, would someone point me to the trivial solution please? :-) So it seems to me that this "template widgets" approach would be more useful for a large, relatively heavyweight project than for something smaller... and my point of view is no doubt coloured by the fact that I'm mostly interested in smaller projects.

    --
    F o x t r o t U n i f o r m
    Found a typo in this node? /msg me
    % man 3 strfry

      Either you copy table.tmpl into every project directory that needs it, or you introduce some scheme for looking these widgets up every time they're referenced

      I consider reuse within a single project to be a sufficient win, so each project gets its own copy of whatever template fragments that project needs. This also allows per-project look-and-feel tweakage.

        I consider reuse within a single project to be a sufficient win, so each project gets its own copy of whatever template fragments that project needs. This also allows per-project look-and-feel tweakage.

        Fair enough, and definitely a win over hand-coding table loops wherever they're needed, but:

        1. Isn't per-project presentation tweakage a CSS problem, not an HTML problem? (Yes, I'm aware that I'm being a bit of an ivory-tower theoretician here, and that CSS isn't a universally applicable solution, but still.)
        2. Per-project templates don't seem to mix well with global code (I presume your tableHTML function is in a module somewhere) -- if your code changes in such a way that it needs an updated table.tmpl, you have to remember to change all the template copies, and that sounds like a monumental pain in the ass if production code is affected.
        Of course, I'm just picking nits here. In general, I like the idea.

        --
        F o x t r o t U n i f o r m
        Found a typo in this node? /msg me
        % man 3 strfry

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://379870]
Approved by FoxtrotUniform
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others meditating upon the Monastery: (5)
As of 2024-03-29 08:41 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found