Recipe 19.7. Formatting Lists and Tables with HTML Shortcuts
Author: Tom Christiansen & Nathan Torkington
Date: 1999-07-02T01:45:36Z

#

| Chapter 19 CGI Programming |

## 19.7. Formatting Lists and Tables with HTML Shortcuts

### Problem

You have several lists and tables to generate and would like helper functions to make these easier to output.

### Solution

The CGI module provides HTML helper functions which, when passed array references, apply themselves to each element of the referenced array:

    print ol( li([ qw(red blue green)]) );
    <OL><LI>red</LI> <LI>blue</LI> <LI>green</LI></OL>
    @names = qw(Larry Moe Curly);
    print ul( li({ -TYPE => "disc" }, \@names) );
    <UL><LI TYPE="disc">Larry</LI> <LI TYPE="disc">Moe</LI>
        <LI TYPE="disc">Curly</LI></UL>

### Discussion

The distributive behavior of the HTML generating functions in CGI.pm can significantly simplify generation of lists and tables. Passed a simple string, they just produce HTML for that string. But passed an array reference, they work on all those strings.

    print li("alpha");
        <LI>alpha</LI>
    print li( [ "alpha", "omega"] );
        <LI>alpha</LI> <LI>omega</LI>

The shortcut functions for lists will be loaded when you use the `:standard` import tag, but you need to ask for `:html3` explicitly to get helper functions for working with tables. There's also a conflict between the `<TR>` tag, which would normally make a `tr()` function, and Perl's built-in `tr///` operator. Therefore, to make a table row, use the `Tr()` function.

This example generates an HTML table starting with a hash of arrays. The keys will be the row headers, and the array of values will be the columns.

    use CGI qw(:standard :html3);

    %hash = (
        "Wisconsin"  => [ "Superior", "Lake Geneva", "Madison" ],
        "Colorado"   => [ "Denver", "Fort Collins", "Boulder" ],
        "Texas"      => [ "Plano", "Austin", "Fort Stockton" ],
        "California" => [ "Sebastopol", "Santa Rosa", "Berkeley" ],
    );

    $\ = "\n";

    print "<TABLE> <CAPTION>Cities I Have Known</CAPTION>";
    print Tr(th [qw(State Cities)]);
    for $k (sort keys %hash) {
        print Tr(th($k), td( [ sort @{$hash{$k}} ] ));
    }
    print "</TABLE>";

That generates text that looks like this:

    <TABLE> <CAPTION>Cities I Have Known</CAPTION>
        <TR><TH>State</TH> <TH>Cities</TH></TR>
        <TR><TH>California</TH> <TD>Berkeley</TD> <TD>Santa Rosa</TD>
            <TD>Sebastopol</TD> </TR>
        <TR><TH>Colorado</TH> <TD>Boulder</TD> <TD>Denver</TD>
            <TD>Fort Collins</TD> </TR>
        <TR><TH>Texas</TH> <TD>Austin</TD> <TD>Fort Stockton</TD>
            <TD>Plano</TD></TR>
        <TR><TH>Wisconsin</TH> <TD>Lake Geneva</TD> <TD>Madison</TD>
            <TD>Superior</TD></TR>
    </TABLE>

You can produce the same output using one print statement, although it is slightly trickier, because you have to use a `map` to create the implicit loop. This print statement produces output identical to that displayed above:

    print table
            caption('Cities I have Known'),
            Tr(th [qw(State Cities)]),
            map { Tr(th($_), td( [ sort @{$hash{$_}} ] )) } sort keys %hash;

This is particularly useful for formatting the results of a database query, as in Example 19.3 (see Chapter 14, Database Access).

#### Example 19.3: salcheck

    #!/usr/bin/perl
    # salcheck - check for salaries
    use DBI;
    use CGI qw(:standard :html3);

    $limit = param("LIMIT");

    print header(), start_html("Salary Query"),
          h1("Search"),
          start_form(),
          p("Enter minimum salary", textfield("LIMIT")),
          submit(),
          end_form();

    if (defined $limit) {
        $dbh = DBI->connect("dbi:mysql:somedb:server.host.dom:3306",
            "username", "password")
            or die "Connecting: $DBI::errstr";
        $sth = $dbh->prepare("SELECT name,salary FROM employees
            WHERE salary > $limit")
            or die "Preparing: ", $dbh->errstr;
        $sth->execute
            or die "Executing: ", $sth->errstr;

        print h1("Results"), "<TABLE BORDER=1>";

        while (@row = $sth->fetchrow_array()) {
               print Tr( td( \@row ) );
        }

        print "</TABLE>\n";
        $sth->finish;
        $dbh->disconnect;
    }

    print end_html();

### See Also

The documentation for the standard CGI module; Recipe 14.10

19.6. Executing Commands Without Shell Escapes |  | 19.8. Redirecting to a Different Location