http://www.perlmonks.org?node_id=193367

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

I've been playing around with nested <TMPL_LOOP> structures in HTML::Template and I've run into some trouble. I have a hash with over 40 keys, and I want to turn it into a set of HTML tables like this:

key key1 key2 key3 key4 key5
value val1 val2 val3 val4 val5
key key6 key7 key8 key9 key10
value val6 val7 val8 val9 val10

Each table should be no more than 6 cells wide, including the <th>'s. I've got the HTML template defined as follows:

<TMPL_LOOP NAME=TABLE> <table> <tr> <th>key</th> <TMPL_LOOP NAME=KEYCELL> <td><TMPL_VAR NAME=KEY></td> </TMPL_LOOP> </tr> <tr> <th>key</th> <TMPL_LOOP NAME=VALCELL> <td><TMPL_VAR NAME=VAL></td> </TMPL_LOOP> </tr> </table> </TMPL_LOOP>

My problem is that I can't for the life of me wrap my head around how to build the array to pass in the $template->param(TABLE => [ @array ]) line of my script. Any help with this would be greatly appreciated.

-rattus
__________
He seemed like such a nice guy to his neighbors / Kept to himself and never bothered them with favors
- Jefferson Airplane, "Assassin"

Replies are listed 'Best First'.
Re: Nesting <TMPL_LOOP> in HTML::Template
by blokhead (Monsignor) on Aug 28, 2002 at 06:19 UTC
    How about something like this? It produces the correct output for me, although you'll probably want the hash keys sorted some way. This may be a little sloppy for some folks, because it uses lots of auto-vivification to create the deep @tables structure. I don't mind, though.
    use HTML::Template; my $template = qq{ <TMPL_LOOP NAME=TABLE> <table border=1> <tr><th>key</th> <TMPL_LOOP NAME=KEYCELL> <td><TMPL_VAR NAME=KEY></td> </TMPL_LOOP> </tr><tr><th>value</th> <TMPL_LOOP NAME=VALCELL> <td><TMPL_VAR NAME=VAL></td> </TMPL_LOOP> </tr> </table> </TMPL_LOOP> }; my %somehash = map(("key$_" => "val$_"), 1..15); my $maxwidth = 6; my @tables = (); my $num_items = 0; for (keys %somehash) { my $table_num = int($num_items++ / $maxwidth); push @{$tables[$table_num]{keycell}}, { key => $_ }; push @{$tables[$table_num]{valcell}}, { val => $somehash{$_} }; } my $template = HTML::Template->new( scalarref => \$template ); $template->param( TABLE=>\@tables ); print $template->output;

    -------------
    This produces the output (keys are unsorted, as you can see):

    key key7 key8 key9 key10 key11 key12
    value val7 val8 val9 val10 val11 val12
    key key13 key14 key1 key15 key2 key3
    value val13 val14 val1 val15 val2 val3
    key key4 key5 key6
    value val4 val5 val6

    I'd be interested to see if any masterful monks can reduce the for loop to a one-liner map() statement (or a two-liner if the keycell and valcell loops must be separated). Hope this helps, and good luck. (BTW, I modified your HTML template a little bit -- added indentation, border=1, and s/key/value/ in 7th line)

    Update: to fit the specifications of your original question, you should change $maxwidth to 5, as you wanted a maximum of 6 cells including the <th> tags.

(jeffa) Re: Nesting <TMPL_LOOP> in HTML::Template
by jeffa (Bishop) on Aug 28, 2002 at 12:51 UTC
    Check out (jeffa) Re: Structure for nested html::template loops.

    UPDATE:
    Here is how i would do it ... this is almost identical to blokhead's answer:

    use strict; use HTML::Template; my $html = do{local $/;<DATA>}; my %hash = map { $_ => chr $_ } (97..122); my ($i,$j) = (0,0); my $max = 5; my $table; for (sort keys %hash) { push @{$table->[$j]->{key}},{key => $_}; push @{$table->[$j]->{val}},{val => $hash{$_}}; $j++ unless ++$i % $max; } my $tmpl = HTML::Template->new( scalarref => \$html, ); $tmpl->param( table => $table ); print $tmpl->output; __DATA__ <style type="text/css"> <!-- table { border-style: outset; border-width: thin; } th { border-style: inset; } td { border-style: inset; } --> </style> <tmpl_loop table> <table border="1"> <tr> <th>Key</th> <tmpl_loop key> <td><tmpl_var key></td> </tmpl_loop> </tr> <tr> <th>Val</th> <tmpl_loop val> <td><tmpl_var val></td> </tmpl_loop> </tr> <table> </tmpl_loop>

    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: Nesting <TMPL_LOOP> in HTML::Template
by jepri (Parson) on Aug 28, 2002 at 11:40 UTC
    This is an excerpt from some production code that I turned out while I was still learning Perl. It could be shorter, but you can see what is happening. To access the inner loop variables, they have to be set as references stored in keys stored in a hash stored in the array that the outer loop is iterating over.

    You can do it without the hash in the middle, but if you do that you have nowhere to store things like data about the row that can be accessed in the outer loop. If that is a little hard to follow, I can recommend you do it the same way I did: build a little test CGI where you build the data structure by hand and get it working with HTML::Template first.

    The upside is that you will really, really know your perlrefs at the end of this.

    for ( outer_loop sets $x, $y) { $row_data{id} = $x; $row_data{text} =$y; my @temp=@{gimmee_fields($x,$dbh)}; foreach (@temp){ push @{$row_data{fields}},{id=>${$_}{id}, text=>${$_}{text}}; } foreach (@{$row_data{fields}}){ my $subscribed=$fields_sth->fetchrow_array; ${$_}{value}="checked" if $subscribed; } push(@prof_loop, \%row_data); } page_out('template', professions=> \@prof_loop); <TMPL_LOOP NAME=professions> <a name="<TMPL_VAR NAME="id">"><TMPL_VAR NAME="text"></a> <TMPL_LOOP NAME=fields> <INPUT TYPE=CHECKBOX NAME="field_<TMPL_VAR NAME="id">" <TMPL_VAR NAME="value"> > <TMPL_VAR NAME="text"> </TMPL_LOOP> </TMPL_LOOP>

    I almost feel sorry for the maintenance programmer.

    ____________________
    Jeremy
    I didn't believe in evil until I dated it.

Thanks! (Re: Nesting <TMPL_LOOP> in HTML::Template)
by rattusillegitimus (Friar) on Aug 29, 2002 at 03:53 UTC

    Thanks! I think I see how your suggestions are working, and will start playing with them shortly. I'll probably also push my understanding of perlrefs by taking jepri's suggestion to build the data structure by hand in a test script. When I finally threw my hands up and posted last night, I had many pieces of paper with the data stuctures drawn or written on them, but no clue how to create them programatically ;)

    -rattus
    __________
    He seemed like such a nice guy to his neighbors / Kept to himself and never bothered them with favors
    - Jefferson Airplane, "Assassin"