I was following this tutorial on the HTML::Template module for Perl. Here's the template:
<!--template2.tmpl-->
<html>
<body>
<table>
<tr>
<th>Language</th>
<th>Description</th>
</tr>
<tmpl_loop name="language">
<tr>
<td><tmpl_var name="language_name"></td>
<td><tmpl_var name="description"></td>
</tr>
</tmpl_loop>
</table>
</body>
</html>
And here's the CGI test program:
#!"C:\Strawberry\perl\bin\perl.exe" -wT
use CGI qw(:all);
use CGI::Carp qw(fatalsToBrowser);
use HTML::Template;
my @rows = (
{
language_name => 'C#',
description => 'Created by Microsoft'
},
{
language_name => 'PHP',
description => 'Hypertext Preprocessor'
},
{
language_name => 'Haskell',
description => 'Functional language'
},
);
print header;
my $template=HTML::Template->new(filename=>'template2.tmpl');
$template->param(language => @rows);
print $template->output();
This fails with the following error: HTML::Template::param() : attempt to set parameter 'language' with a scalar - parameter is not a TMPL_VAR!
However, when I change the definition of @rows from using parenthesis to using square brackets(from my @rows=(...)
to my @rows = [...]
) the code works fine; it displays a table with the data.
As I understood from reading this article, the first form is an array defined from a list and the second one is a reference to an anonymous array. It's still not clear to me why the first form doesn't work. I'd appreciate you clarifying this for me.
The tutorial you're following contains an error. The line
$template->param( language => @languages );
should be
$template->param( language => \@languages );
Why? Short answer: the right-hand side of the loop name you pass to param
must be a reference to an array, not an array.
Long answer: When you pass arguments to a function or method, all of the arguments get expanded into one long list. This is a common source of mistakes for beginners. So in your code (and in the tutorial's code), you're not passing two parameters to the param
method, you're passing four (one for the string 'language', and three for the elements of @languages.
Here's an example of this argument-list unraveling. If you have three variables as follows:
my $scalar = 'bear';
my @array = ('rat', 'moose', 'owl');
my %hash = (mass => 500, units => 'kg');
and you pass them to a function like so:
some_function($scalar, @array, %hash);
then the function will see eight arguments: 'bear'
, 'rat'
, 'moose'
, 'owl'
, 'mass'
, 500
, 'units'
, and 'kg'
! Perhaps even more surprising, the two sets of values from the hash might be passed in a different order, because hashes are not stored or retrieved in a determinate order.
Your solution of changing the parentheses to square brackets works, but not for a very good reason. Parentheses delimit lists (which can be stored in arrays or hashes); square brackets delimit references to arrays. So your square-bracket code creates a reference to an anonymous array, which is then stored as the first (and only) element of your named array @rows
. Instead, you should either store the array reference (delimited with square brackets) in a scalar variable (say, $rows
), or you should use parentheses, store the list in the array @rows
, and pass a reference to that array to the param
method (by using a backslash, as I did above with \@languages
).