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

I have been following this tutorial for creating dynamic content with Template and trying to apply this to my own needs. But I've hit a problem in preparing the data for the template.

Template file

[% SET menuframe = ' menuselect' %] [% PROCESS admin_menu.tt %] [% FOREACH frame IN frames %] <hr> <p><b>[% frame.name %]</b><br> [% frame.colour %]</p> [% END %]
This does not generate any output for anything within the [% FOREACH ... %] block

Here is the code that extracts some data from the database and prepares it for passing to the Template display code

#!/usr/bin/perl use Site::Common; use Site::HTML; use strict; my $site = Site::Common->new; my $html = Site::HTML->new; my $dbh = $site->db; if ($data{'command'} eq 'frames') { my $vars = { 'frames' => \&list_frames, }; warn "Displaying template"; # This gets called $html->display("admin_frames", $vars); } else { $html->display("admin_pictures"); } sub list_frames { warn "Building list of frames"; # This is not called my @frames; my $query = $dbh->prepare("SELECT idFrame, name, colour FROM Frame +"); $query->execute(); while (my ($id, $name, $colour) = $query->fetchrow_array()) { my $frame = { 'id' => $id, 'name' => $name, 'colour' => $colour, }; push @frames, $frame; } return \@frames; }
There are a couple of warn statements in there so I can see something of what the code is doing.

Here is the relevant part of Site::HTML...

package Site::HTML; use Template; use Site::Variables; use strict; my $template = Template->new(INCLUDE_PATH => $Site::Variables::templat +e_path); sub display { my $self = shift; my $file = shift; my %vars = @_; $template->process("$file.tt", \%vars); } 1;

I'm not sure when list_frames is supposed to get called but I'm guessing at the time the hash reference $vars is generated.

Replies are listed 'Best First'.
Re: Preparing data for Template
by 1nickt (Abbot) on Dec 30, 2020 at 23:50 UTC
      It won't get called unless you call it ;-)

      I was kind of assuming that the Template code calls the subroutine at some point! After all, it gets passed the reference...

      In the example code, get_user_projects doesn't seem to be explicitly called.

      #!/usr/bin/perl use strict; use warnings; use Template; use CGI; $| = 1; print "Content-type: text/html\n\n"; my $file = 'userinfo.html'; my $vars = { 'version' => 3.14, 'days' => [ qw( mon tue wed thu fri sat sun ) ], 'worklist' => \&get_user_projects, 'cgi' => CGI->new(), 'me' => { 'id' => 'abw', 'name' => 'Andy Wardley', }, }; sub get_user_projects { my $user = shift; my @projects = ... # do something to retrieve data return \@projects; } my $template = Template->new({ INCLUDE_PATH => '/home/abw/websrc/src:/home/abw/websrc/lib', PRE_PROCESS => 'config', }); $template->process($file, $vars) || die $template->error();

        In the example code, get_user_projects doesn't seem to be explicitly called.

        Not in the Perl. But in the template, the line [% FOREACH project IN worklist(me.id) %] calls worklist as a function with the appropriate ID as its argument.

        I would assume that in your code, if you don't change to 'frames' => list_frames() (as already suggested), then you would need to call the frames as a function rather than treating it as a variable: probably like [% FOREACH frame IN frames() %] (untested)

        (But it seems to me that you shouldn't need to call a function from the template, unless the data will change based on some parameter from the template, like the me.id argument in the tutorial example. Since you're not feeding anything back to the function, why not just define frames as the arrayref rather than the coderef?)

      Hope this helps!

      Yes thank you, it does help a lot.

      However, it makes me wonder how the code in the example works...I was trying to replicate this arrangement as a learning exercise to understand dynamically preparing data based on the template file.