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

Monks,

I've got HoHoH data I'm trying to display on a web page using HTML::Template. What I want to see is a table where the top row contains my headings and the remaining rows contain the values from the HoHoH. The headings I've put in the HTML::Template code itself and that looks fine but I've been unable to get the values out of my data structure into the correct format for HTML::Template. What I see is the last value of the inner most  for my $key ( sort keys %{$HoH{$user}->{$machine} })  { loop data all on one long row nine times. I've removed some of data so as not to mess up the display in monk's browsers but it looks something like this.

Machine Key One Key Two Key Three
machine3 value9 value9 value9 machine3 value9 value9 value9 machine3 value9 value9 value9

I want it to look like this:
Machine Key One Key Two Key Three
machine1 value1 value2 value3
machine2 value4 value5 value6
machine3 value7 value8 value9

I think the problem is how I'm trying to prepare the data for an HTML::Template loop. The docs say you pass to param() a list (an array ref) of parameter assignments (hash refs) for this loop.

I think I've done that with this line push @{$loop_data},  \%row_data ;.
When I look at \%row_data by inserting a print statement and viewing from the cmd line it looks like what I think should work. When I look at $loop_data either on the web page or with Data::Dumper, I'm only seeing the last row(plus additional stuff with Data::Dumper.

I'm really not sure where the problem is. It could be that the data I'm trying to display should be presented differently, but my experiments with similar data (not HoHoH) lead me to believe this should work. The HTML formatting itself is kind of rough but for this stage I think it's ok. Here's the script:

#!c:\perl\bin\perl.exe #=========================================================== # DESCRIPTION: Take data from DBM::Deep for presentation on www page +. #=========================================================== use strict; use warnings; use CGI; use HTML::Template; my $CGI = CGI->new(); my $user; my $machine; my $loop_data; my %row_data; my %HoH = ( user1 => { machine1 => { key1 => "value1", key2 => "value2", key3 => "value3" }, machine2 => { key1 => "value4", key2 => "value5", key3 => "value6" }, machine3 => { key1 => "value7", key2 => "value8", key3 => "value9" }, } ); for $user ( keys %HoH ) { for $machine ( sort keys %{ $HoH{$user} } ) { $row_data{MACHINE} = $machine; for my $key ( sort keys %{ $HoH{$user}->{$machine} } ) { $row_data{DATA} = $HoH{$user}->{$machine}->{$key}; push @{$loop_data}, \%row_data; } } } # read the template as a scalar from DATA my $html = do { local $/; <DATA> }; # prepare the template and substitute the values my $template = HTML::Template->new( scalarref => \$html ); $template->param( THIS_LOOP => $loop_data ); # print the data print $CGI->header(); print $template->output(); __DATA__ <html> <head> <title>Inventory</title> </head> <body> <h1>machines</h1> <table border="3"> <tr> <th>Machine</th> <th>Key One</th> <th>Key Two</th> <th>Key Three</th> </tr> </table> <table border="1"> <tr> <TMPL_LOOP NAME=THIS_LOOP> <td> <TMPL_VAR NAME="MACHINE"> </td> <td> <TMPL_VAR NAME="DATA"> </td> <td> <TMPL_VAR NAME="DATA"> </td> <td> <TMPL_VAR NAME="DATA"> </td> </TMPL_LOOP> </tr> </table> </body> </html>
I haven't included all the things I've tried and seemingly I've tried a million things. Any comments are welcome.

Thank-you for your consideration.

Replies are listed 'Best First'.
Re: Getting data ready for HTML::Template
by Unforgiven (Hermit) on Nov 18, 2009 at 21:24 UTC
    The first thing I noticed is that in one loop in the template, you've got three tokens all with the same name, so they'd get the same data. In your for loop in Perl, and in the template loop, give each of them different names (key1, key2, key3, or hopefully something more meaningful). You're also pushing to the array (in other words, making a row to display) once per hash key - you actually want one row per machine. This is entirely untested code, but give this a try. Set the template tokens to match your data's keys (key1,key2,key3), and try this loop.
    for $user ( keys %HoH ) { for $machine ( sort keys %{ $HoH{$user} } ) { %row_data = (); $row_data{MACHINE} = $machine; for my $key ( sort keys %{ $HoH{$user}->{$machine} } ) { $row_data{$key} = $HoH{$user}->{$machine}->{$key}; } push @{$loop_data}, \%row_data; } }
      Thanks unforgiven. Your untested(but valuable) code gave me a piece that I was wondering about(incrementing the hash key) and allowed me to see that at least I'm doing the basics correct because it got me a bit further. I made the suggested changes plus changed my template slightly like this:
      <table border="1"> <tr> <TMPL_LOOP NAME=THIS_LOOP> <td> <TMPL_VAR NAME="MACHINE"> </td> <td> <TMPL_VAR NAME="key1"> </td> <td> <TMPL_VAR NAME="key2"> </td> <td> <TMPL_VAR NAME="key3"> </td> </tr> </TMPL_LOOP> </table>

      New output:

      Machine Key One Key Two Key Three
      machine3 value7 value8 value9
      machine3 value7 value8 value9
      machine3 value7 value8 value9

      Now it seems like I just need to figure out the correct logic in my loop that creates the data.

        The remaining problem is that you push \%row_data into your array, which is the same pointer to the same data structure (which gets written to three times until only the last row data remains) instead of a new one. The easiest way to generate a new hash every time through the loop is to change the third line of Unforgivens loop
        %row_data = ();
        to
        my %row_data = ();
Re: Getting data ready for HTML::Template
by spx2 (Deacon) on Nov 19, 2009 at 10:19 UTC