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

Greetings fellow monks,

I have an interesting style question that may belong more under Meditations than Seekers, but since it's a question, here it is: I've been using HTML::Template both independantly and along with CGI::Application for a while now in our organization. I love it. (If you haven't played around with it, I strongly recommend it.) One of the things I love about this is loop context style. If I'm generating a report that'll get dumped into a table, I use loop context to alternate the background shade color of each row. (I got this idea from the old TSR AD&D books.)

Here's my question: I've come up with three options for doing this, and I'm not sure which is the "best" or if there's another that's "better" still. I've been using the first option up 'til now. The idea is to setup a CSS to define the table cell background colors, then reference each definition using the loop context var "__ODD__".

Example code, option #1

<STYLE type="text/css"><!-- TD.odd { background-color: #c0c0c0; } TD.even { background-color: #e0e0e0; } --></style> <TABLE border=0 cellspacing=0 cellpadding=3> <TMPL_LOOP name="some_loop"> <TMPL_IF name="__ODD__"> <TR> <TD class=odd><TMPL_VAR name="content_column_1"></td> <TD class=odd><TMPL_VAR name="content_column_2"></td> <TR> <TMPL_ELSE> <TR> <TD class=even><TMPL_VAR name="content_column_1"></td> <TD class=even><TMPL_VAR name="content_column_2"></td> <TR> </TMPL_IF> </TMPL_LOOP> </table>

OK, fairly straight forward. I've been using this option for quite a while, but the thing that's always annoyed me has been the fact that I've had to repeat the same HTML code. So option #2 tries to get around that by defining the TMPL_IF .. TMPL_ELSE for the loop context inside the TD tag.

Example code, option #2

<TABLE border=0 cellspacing=0 cellpadding=3> <TMPL_LOOP name="some_loop"> <TR> <TD <TMPL_IF name="__ODD__">class=odd<TMPL_ELSE>class=even</TMPL +_IF>> <TMPL_VAR name="content_column_1"> </td> <TD <TMPL_IF name="__ODD__">class=odd<TMPL_ELSE>class=even</TMPL +_IF>> <TMPL_VAR name="content_column_2"> </td> <TR> </TMPL_LOOP> </table>

Now this is much better, I think, except for a couple things: First off, the line with the TD is a little difficult to read. The whole reason I started using HTML::Template in the first place was to let inexperienced coders maintain our sites. I know Perl, but few others in my group do. (They're learning, of course.) They know HTML, though, so they can support sites that are coded with HTML::Template and look fairly straight forward. The other thing I don't like about the above code is that it requires TMPL_IF .. TMPL_ELSE logic to happen for every single cell in every single row. Seems like there's a better way.

Example code, option #3

<STYLE type="text/css"><!-- TD.loop1 { background-color: #c0c0c0; } TD.loop { background-color: #e0e0e0; } --></style> <TABLE border=0 cellspacing=0 cellpadding=3> <TMPL_LOOP name="some_loop"> <TR> <TD class=loop<TMPL_VAR name="__ODD__">> <TMPL_VAR name="content_column_1"> </td> <TD class=loop<TMPL_VAR name="__ODD__">> <TMPL_VAR name="content_column_2"> </td> <TR> </TMPL_LOOP> </table>

So this code assumes that the loop context var "__ODD__" returns a 1 or nothing. So I just define the names of the TD style definitions to match what's expected. If ever "__ODD__" changes to return, say, a 1 or 0, then I'd have a very little recoding to do. Other than that, I think this is the best option.

So, what do you think?

-gryphon
code('Perl') || die;

Replies are listed 'Best First'.
(jeffa) Re: Loop Context Style with HTML::Template
by jeffa (Bishop) on Aug 02, 2002 at 23:17 UTC
    I am with Bird on this one gryphon (although i do really like fruiture's suggestion). Here is why - right now you have two colors for two kinds of rows, but what happens when someone in charge decides that they want three colors, or four or five? __ODD__ isn't going to cut it anymore. By utilizing the modulus (%) operator, you can easily loop through a list of 'classes'. Consider this (and replace the single quotes with double quotes if you are running this on Win32 system):
    perl -le '@a = a..d; print $a[$i++ % @a] for 0..15'
    Couple this with a map and you can add a different class for every row of data. Now, i don't agree with storing the style sheet info inside the script, after all, you want the others in your group to maintain that part. Having said all of that, here is my example:
    use strict; use HTML::Template; my $data = do {local $/; <DATA>}; my $tmpl = HTML::Template->new(scalarref => \$data); my $rows = [ { col1 => 'Graham Chapman', col2 => 'John Cleese' }, { col1 => 'Terry Gilliam', col2 => 'Eric Idle' }, { col1 => 'Terry Jones ', col2 => 'Michael Palin' }, { col1 => 'Terry Jones ', col2 => 'Michael Palin' }, { col1 => 'Terry Gilliam', col2 => 'Eric Idle' }, ]; my $i = 0; my @class = qw(one two three); $tmpl->param( rows => [ map { { %$_, class => $class[$i++ % @class] } } @$rows ], ); print $tmpl->output; __DATA__ <style type="text/css"> <!-- table { border-spacing: 0px; } td { padding: 3px; } td.one { background-color: #c0c0c0; } td.three { background-color: #e0e0e0; } td.two { background-color: #d0d0d0; } --> </style> <table> <tmpl_loop rows> <tr> <td class="<tmpl_var class>"> <tmpl_var col1> </td> <td class="<tmpl_var class>"> <tmpl_var col2> </td> <tr> </tmpl_loop> </table>
    So, the code is a little more complex, but not terribly more and definitely at the expense of making the template less complex. I am sure the others in your group will appreciate it. :)

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    
Re: Loop Context Style with HTML::Template
by fruiture (Curate) on Aug 02, 2002 at 20:50 UTC

    option #2 is imho the best way to do it. Who said that it must all stand in one line?

    <TD class= <TMPL_IF name="__ODD__">"odd" <TMPL_ELSE>"even" </TMPL_IF> >
    --
    http://fruiture.de
Re: Loop Context Style with HTML::Template
by Bird (Pilgrim) on Aug 02, 2002 at 21:02 UTC
    I would definitely go with option 3, but why not take it one step further? Rename your "__ODD__" template variable to something like "class", then store the full class name instead of just a "1" or "" in that variable. Your HTML then looks like this...
    <STYLE type="text/css"><!-- TD.loop1 { background-color: #c0c0c0; } TD.loop { background-color: #e0e0e0; } --></style> <TABLE border=0 cellspacing=0 cellpadding=3> <TMPL_LOOP name="some_loop"> <TR> <TD class=<TMPL_VAR name="class">> <TMPL_VAR name="content_column_1"> </td> <TD class=<TMPL_VAR name="class">> <TMPL_VAR name="content_column_2"> </td> <TR> </TMPL_LOOP> </table>
    At this point you can just alternate between values of "loop" or "loop1" in the "bg_class" key in your loop array. If you want to take that one step further, you could pull the class names and background colors themselves into your script, and replace them with template variables in the HTML. This way you could use an array of class name/color pairs which would be accessed by both the style section and the loop data structure. This would allow you to expand to an arbitrary number of background colors to be cycled through. Something like...
    <STYLE type="text/css"><!-- <TMPL_LOOP name="style_loop"> TD.<TMPL_VAR name="class"> { background-color: <TMPL_VAR name="color"> +; } --></style>
    ...for the style section. And...
    my @styles = ( { class => "bg_red", color => "#FF0000" }, { class => "bg_green", color => "#00FF00" }, { class => "bg_blue", color => "#0000FF" } );
    ...for the styles array. Then the main loop would use $styles[$n]->{class} to fill it's data structure, with $n cycling through each element of @styles.

    Whoa... okay, so I got a little carried away here. This is probably more than you need, but that's where I'd go with it.

    -Bird

      I would definitely go with option 3, but why not take it one step further? Rename your "__ODD__" template variable to something like "class", then store the full class name instead of just a "1" or "" in that variable.

      Greetings Bird,

      The problem with that is that the <TMPL_VAR name="__ODD__"> is a loop context variable. It's created and controlled by HTML::Template as the module loops through the array passed to it by reference. I don't think I can mess with it without violating some OOP law.

      I could define "class" as an element of the hash of the array of the "some_loop" loop, but one of the reasons I liked the HTML::Template concept was that it didn't require me to do any such defining since the module's code handled everything.

      -gryphon
      code('Perl') || die;

        Right, sorry, I didn't realize you were so set on using the loop context variable. By creating one of my own, I provided a more versatile solution which is, as I mentioned, probably more than you need.

        Oh well... :)

        -Bird

Re: Loop Context Style with HTML::Template
by DamnDirtyApe (Curate) on Aug 02, 2002 at 21:09 UTC

    I'm afraid I'm not too familiar with HTML::Template, but this post motivated me to try solving this problem with Template Toolkit.

    <table border="0" cellspacing="0" cellpadding="3"> [% row = 0 %] [% FOREACH table_row %] [% row_bg = row % 2 ? 'c0c0c0' : 'e0e0e0' %] <tr> <td style="background-color: [% row_bg %]">[% content_column_1 % +] </td> <td style="background-color: [% row_bg %]">[% content_column_2 % +] </td> </tr> [% row = row + 1 %] [% END %] </table>

    Not exactly an answer to your question, but I thought I'd post it nonetheless. :-)


    _______________
    D a m n D i r t y A p e
    Home Node | Email
      Agreed, while not an answer, it is another option - I have come to use Template Toolkit almost exclusively for my production work and have written about how to easily incorporate this into the CGI::Application framework here.