Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked
 
PerlMonks  

HTML::Template : How to separate code and html with lesser maintenance issues

by sara2005 (Scribe)
on Nov 21, 2006 at 04:25 UTC ( #585194=perlquestion: print w/replies, xml ) Need Help??

sara2005 has asked for the wisdom of the Perl Monks concerning the following question:

Dear Monks,

Below is a simple cgi script I wrote to display different messages based on the value for $item using HTML Template

Everything worked fine and I didn't think about raising this question here until I read the following from HTML::Template Documentation

WARNING: Much of the benefit of HTML::Template is in decoupling your Perl and HTML. If you introduce numerous cases where you have TMPL_IFs and matching Perl if()s, you will create a maintenance problem in keeping the two synchronized. I suggest you adopt the practice of only using TMPL_IF if you can do so without requiring a matching if() in your Perl code.

Below is the script and template:-

use CGI qw/:standard :delete_all :escapeHTML :html3 :all/; use HTML::Template; print header; my $item = "CONTINUE"; my ($item1_val, $item2_val, $item3_val ) = 0; if ($item eq "SELECT") { $item1_val = 1; } elsif ($item eq "CONTINUE") { $item2_val = 1; } elsif ($item eq "DENIED"){ $item3_val = 1; } my $template = HTML::Template->new( filename => 'template4.tmpl' ); $template->param( item1 => $item1_val ); $template->param( item2 => $item2_val ); $template->param( item3 => $item3_val ); print $template->output();

Template:-

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>HTML::Template example</title> </head> <body> <TMPL_IF name = "item1"> <table align="center"> <tr><td align="center"> <font style="font-size: 14pt; font-family: Arial; solid #CCC; padding: + 3px" color="blue"> <p>Please Select an Item</p></font> </td></tr> </table> </TMPL_IF> <TMPL_IF name = "item2"> <table align="center"> <tr><td align="center"> <font style="font-size: 14pt; font-family: Arial; solid #CCC; padding: + 3px" color="blue"> <p>Item Selected: Car</p></font> </td></tr> <tr><td align="center"> <font style="font-size: 8pt; font-family: Arial; solid #CCC; padding: +3px" color="black"> <p>(Please click to continue)</p></font> </td></tr> <tr><td align="center"> <input type="button" value="submit"/> </td></tr> </table> </TMPL_IF> <TMPL_IF name = "item3"> <table align="center"> <tr><td align="center"> <font style="font-size: 14pt; font-family: Arial; solid #CCC; padding: + 3px" color="blue"> <p>Item Selected: Mini Van</p></font> </td></tr> <tr><td align="center"> <font style="font-size: 12pt; font-family: Arial; solid #CCC; padding: + 3px" color="red"> <b>Access to this Item is denied</b></font> </td></tr> </table> </TMPL_IF> </body> </html>

As you can see, I end up in a situation, where I have matching 'if' and 'TMPL_IF' loops in the perlscript and template, respectively

The other way I could think is to have a variable (say $html_output) in the script and use the cgi module to append the appropriate html code to the variable and finally display that using HTML::Template as follows:-

use CGI qw/:standard :delete_all :escapeHTML :html3 :all/; use HTML::Template; print header; my $item = "CONTINUE"; if ($item eq "SELECT") { $html_output = ...... } elsif ($item eq "CONTINUE") { $html_output = ...... } elsif ($item eq "DENIED"){ $html_output = ...... } my $template = HTML::Template->new( filename => 'template4.tmpl' ); $template->param( html_output => $html_output ); print $template->output();

corresponding template...

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>HTML::Template example</title> </head> <body> <TMPL_VAR name = "html_output"> </body> </html>

In the above approach,I would be mixing scripts and html tags, which I hate to do.

Would you please suggest some ideas to handle this?

Replies are listed 'Best First'.
Re: HTML::Template : How to separate code and html with lesser maintenance issues
by jbrugger (Parson) on Nov 21, 2006 at 05:51 UTC
    You could choose to create 3 different templates for select, continue or denied.
    my $tmpl = ""; my $item = "CONTINUE"; if ($item eq "SELECT") { $tmpl = "select.tmpl"; } elsif ($item eq "CONTINUE") { $tmpl = "continue.tmpl"; } elsif ($item eq "DENIED"){ $tmpl = "denied.tmpl"; } my $template = HTML::Template->new( filename => $tmpl );
    So no ifs are needed in your template (i try to put as less logic as possible in my templates as i can)
    Do you want (simple) logic? Try HTML::Template::Expr.

    However, you may wonder if you need it. Another nice trick you can do with HTML::Template; call a perl function by using parameters, so you don't even need to register a function like in HTML::Template::Expr.
    # template.tmpl <TMPL_VAR NAME="function_xyz"> # Perl: my $tmpl = HTML::Template->new( filename => 'template.tmpl' ); my @all_params = $tmpl->param(); foreach (@all_params) { if ($_ =~ /^function_/i) { my ($a,$b) = split (/_/,$_,2); $tmpl->param($_ => $b() ); } } sub xyz { return "template value needed"; }


    "We all agree on the necessity of compromise. We just can't agree on when it's necessary to compromise." - Larry Wall.
      if ($item eq "SELECT") { $tmpl = "select.tmpl"; } elsif ($item eq "CONTINUE") { $tmpl = "continue.tmpl"; } elsif ($item eq "DENIED"){ $tmpl = "denied.tmpl"; }

      In which case (I know you're adapting the OP's code, but) one would probably use a hash instead, especially if other options were possibly to be added. But of course this is just a complete side note.

      $tmpl->param($_    => $b() );
      what's this? this gives me a syntax error. do you wanted to use a subroutine-reference instead? like main->can($b)?
        hmm, yes, it's just an example i stripped from my own code, where i use <TMPL_VAR NAME="function_module_functionname">. there i do the following:
        eval("require $module"); $mod = $module->new(); $tmpl->param($_ => $mod->$function())
        But i see you got the idea :)
        "We all agree on the necessity of compromise. We just can't agree on when it's necessary to compromise." - Larry Wall.
Re: HTML::Template : How to separate code and html with lesser maintenance issues
by tinita (Parson) on Nov 21, 2006 at 12:19 UTC
    you could use HTC's switch/case tag.
    HTC doesn't have expressions yet, but switch/case allows simple string comparison.

    <tmpl_switch name="item"> <tmpl_case SELECT> ... <tmpl_case CONTINUE> ... <tmpl_case DENIED> ... </tmpl_switch>

      Thanks for the info.

      But the documentation for HTC says that it could slow down things under certain circumstances, which I don't want. I would rather try to do things with just H::T

      The best performance gain is probably reached in applications running under mod_perl, for example. If you don't use memory caching (e.g. CGI environment), HTC will be even slower than H::T. If you don't use caching at all (e.g. CGI environment without file caching), HTC will be much slower than H::T and TT

        But the documentation for HTC says that it could slow down things under certain circumstances
        yes, if you are in a CGI environment and you can't use filecaching, for whatever reason.
        but in that case your script will be slow anyway because the interpreter has to start it all over again every request, so the speed difference won't be that big compared to the overall execution time.
        and last but not least, if speed is *that* crucial for you you should switch to mod_perl or FastCGI anyway.
Re: HTML::Template : How to separate code and html with lesser maintenance issues
by perrin (Chancellor) on Nov 21, 2006 at 19:40 UTC
    It doesn't look like there's a good reason for all three of those to be in the one template. Your example above would be fine and contain no HTML if you made those $html_output assignments call small templates to generate their HTML, rather than in-lining it. Or you could pull the header and footer stuff out into includes, so you'd just call different main templates for each case and they would all make include calls for the header and footer.

      I created some templates like

      small_template.tmpl

      <table align="center"> <tr><td align="center"> <font style="font-size: 14pt; font-family: Arial; solid #CCC; padding: + 3px" color="blue"> <p>Please Select an Item</p></font> </td></tr> </table>

      and then read them into $html_output by

      ... ... open (FILE, "<", "small_template.tmpl" ); $html_output = join "", <FILE>; close FILE; ... ...

      This works great but if I wanted to display some values of variables (like , the selected item's name ) using the small_template.tmpl, it doesn't work.

      Probably, I should have some subrotines to create the html code and then display that using the template. I think it will mix the logic and the html, but it seems inevitable. The best I can do is to have subroutines that create the html messages separated from the logic.

        If you want to display some variables, just run the small templates through HTML::Template instead of reading them directly. There's no reason you can't call it more than once.

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others browsing the Monastery: (1)
As of 2021-12-02 01:10 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    R or B?



    Results (16 votes). Check out past polls.

    Notices?