Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris
 
PerlMonks  

Yet Another Stupid CGI Question

by chaoticset (Chaplain)
on Oct 19, 2001 at 05:08 UTC ( #119850=perlquestion: print w/ replies, xml ) Need Help??
chaoticset has asked for the wisdom of the Perl Monks concerning the following question:

I'm hideously new to the CGI module, and I've been trying to *read* through the docs. Not fun.

However, in between reading the docs, I try to slap a little code together, figuring that getting it on my hands will teach me better than getting it on my eyes (at first).

Now, I know how to do something to each instance of an array.

foreach $thing (@thingies) { something($thing) }

And I've seen how to generate a table from the CGI documentation. (And I won't duplicate it here since I'm working with the copied code before I try to write my own.)

The question: You can't interpolate whole subs, right? So if I do a print statement with a foreach inside it, that's not going to work. Should I slap it all into a hash, or is can I print pieces separately and put the table-stuff *around* the foreach, printing each little chunk of the array as three different table elements by passing them into a short, three-element array?

Is all of this going to make for a prohibitively long return time, considering that the array is going to have 1000+ elements?

Should I give up, and go back to my old job at Subway?

(The last is rhetorical. I know I should go back. I just don't want to.)

Apologies in advance for the time this wastes, and here's hoping I have better questions soon enough.

Comment on Yet Another Stupid CGI Question
Re: Yet Another Stupid CGI Question
by Zaxo (Archbishop) on Oct 19, 2001 at 05:41 UTC

    print can take a list or array of things to print:

    print map {something($_)} @thingies;
    There are many other ways. Keep on trying things, perl gives you lots of choices besides Subway ;-)

    After Compline,
    Zaxo

Re: Yet Another Stupid CGI Question
by Armos (Scribe) on Oct 19, 2001 at 05:48 UTC
    I too am learning CGI... it's a little crazy at times, but it certainly can do some really neat stuff. I'm not certain exactly what you are asking. When I worked with CGI, I had trouble outputing complex tables, so I usually ended up printing raw HTML. I ended up using CGI to handle the parameters, and then generated any HTML tags myself.

    To clarify a little, you are trying to use the tr and td subs with the print function to generate a complex table? And you are wondering how to get it to work?

    -A
Re: Yet Another Stupid CGI Question
by mandog (Curate) on Oct 19, 2001 at 06:32 UTC
Meta-reply
by Fletch (Chancellor) on Oct 19, 2001 at 08:02 UTC

    Not a direct answer to your question, but you probably don't want to return a page with a table with 1000+ rows in it. For one thing, you may kill some browsers. If it doesn't kill the browser, it'd probably take aeons to render it.

    A better solution might be to return just a subset of the records (10, 25, or 50 are popular choices) and provide a means to page through the results (pass along an extra couple of paramters that are the chunk size and the current location in the result set).

      While your answer is neater I was recently forced by a client to return tables with hundreds of cells (against my advice). The entire page ran to greater than 80kb. It took about fifteen seconds to download over a modem but all modern browsers handled it fine. Netscape waits until the download is finished before it renders but that isn't too bad.

      The funny bit was where the client tried to blame me for the slow download speed.

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

Re: Yet Another Stupid CGI Question
by chaoticset (Chaplain) on Oct 19, 2001 at 09:19 UTC
    Sorry - I should have been more specific about what I'm doing. This should address the questions.

    When the user first clicks on the page, it will dump a text box in front of them, saying 'Enter the first few letters.' Based on that, the program will load and weed out all the entries in the 1000+ array that have those first few letters. Then, it returns a page with the text box at the top(so they know what they typed in) and *just* the matching entries.

    Based on the distribution of beginning letters, I'd say they'd never get a return page with more than 100 entries on it. (At most, maybe 200.) I realize those are stretching, but I honestly don't think they'd happen often. (If they did during testing, I could build a page-breaker and make it return two pages.)

    Now, I realize all this is *way* too much to ask for. I'm not asking for it. All I'd like is a little advice on how to handle turning this data into a web page.

    It's going to be:

    name=cost=number

    And it will come out in a table with a few other things: (chk = checkbox, txt = textfield)

    --------------------------------
    |chk|name|cost|number|txt|
    --------------------------------

    Now, as I understand Perl, it doesn't interpolate while or for loops. So if I make the statement

    print table(...
    ...
    foreach ... {
    ...
    ...
    }
    ...
    )

    then my code won't work, because the for hasn't been dealt with before the print tries to print.

    Am I wrong? Are these things dealt with automagically, and Perl is just all the more amazing? I'm thinking it's not. Does this mean I have to do the table tags by hand?

      My /50 is to second the monk who said HTML::Template. In fact I don't think it's terribly difficult to learn, certainly not to get it to do what you want. You'd make a template file, say you call it results.tmpl which wd look something like:
      <table bgcolor=#66aaff border=2 bordercolor=#ffffff cellspacing=0 cell +padding=6> <TMPL_LOOP RESULTS> <tr> <td> <TMPL_VAR CHK> </td> <td> <TMPL_VAR NAME> </td> <td> <TMPL_VAR COST> </td> <td> <TMPL_VAR NUMBER> </td> <td> <TMPL_VAR TXT> </td> </tr> </TMPL_LOOP> </table>
      (I like writing my TMPLs in upper case and my HTML in lower case to tell them apart visually - no other reason).

      Then in your perl you need a bit that looks like this
      #!/usr/bin/perl -w use strict; use CGI qw(:standard); use HTML::Template; # my guess about what your data structure might be: my %information = ( txt_foo => ['chk_foo','name_foo','cost_foo','number_foo'], txt_bar => ['chk_bar','name_bar','cost_bar','number_bar'], txt_baz => ['chk_baz','name_baz','cost_baz','number_baz'], ); # initialise the template and other vars: my $results_tmpl = HTML::Template->new(filename => 'results.tmpl'); my @results; # populate @results with the data from %information: for (keys %information) { my %row_data; $row_data{'txt'} = $_; $row_data{'chk'} = $information{$_}[0]; $row_data{'name'} = $information{$_}[1]; $row_data{'cost'} = $information{$_}[2]; $row_data{'number'} = $information{$_}[3]; push @results, \%row_data; } # drop the info into the template: $results_tmpl->param(results => \@results); # print the page: print header, start_html, $results_tmpl->output(), end_html;
      Simple, really. The marginally tricky bit is getting the data into the right form to go into the template. The single line $results_tmpl->param(results => \@results); is doing duty for a lot of lines like
      $results_tmpl->param( results => ( txt => 'txt_foo', chk => 'chk_foo', ......
      But obviously if you had to write it all out by hand, you might as well not be using the template at all. What I'm saying is that the code for populating the template will work, but it may take a bit of effort to get your head around it (it did mine). But you said you wanted to get it on your hands...

      The only other problem I can foresee is that for some reason HTML::Template doesn't seem to be part of the standard issue. If you're using an ISP that doesn't offer it, you may want to refer to these threads.

      update :Reflectingthat this is a problem I quite often come up against, and for which I wd like a simple solution, I had a sudden rush of blood to the head and wrote a module that makes it easier to do printing out lists of info in CGI, so now there's yet One More Way To Do It. Now you can do this, which I think you'll agree has a certain classical simplicity:
      #!/usr/bin/perl -w use strict; module library use CGI qw(:standard); use CGI::tab; my %information = ( txt_foo => ['chk_foo','name_foo','cost_foo','number_foo'], txt_bar => ['chk_bar','name_bar','cost_bar','number_bar'], txt_baz => ['chk_baz','name_baz','cost_baz','number_baz'], ); print header, start_html, start_tabs(75,150,225,300); for my $key (keys %information) { print $key,t; print $_,t for @{ $information{$key} }; } print end_tabs, end_html;


      George Sherston

      A couple of things that might make your life a bit easier:

      1. The map function can often be used as an "inline foreach".
      2. In CGI.pm, the Tr and td functions (amongst others) have a neat feature whereby if you pass them a reference to an array, then they return a <tr> or <td> tag for each element in the array.

      I don't know what your data structure looks like, but this sample code might help:

      my @list = ('chk1|name1|cost1|number1|txt1', 'chk2|name2|cost2|number2|txt2', 'chk3|name3|cost3|number3|txt3'); print table(Tr([map { td([split /\|/, $_])."\n" } @list ]));
      --
      <http://www.dave.org.uk>

      "The first rule of Perl club is you don't talk about Perl club."

      There's a bunch of ways to do this. Here's two.
      # One Way my $table_dat = undef; foreach my $ell (@elements){ $table_dat.=Tr(td(b("Label")),td($ell)); # Whatever you need to ou +tput. } print table($table_dat); # Second way use CGI qw/:standard *table/; # Import start_table and end_table print start_table # Data processing here to spit out rows and cells... foreach my $ell (@elements){ print Tr(td(b("Label")),td($ell)); } print end_table


      -Lee

      "To be civilized is to deny one's nature."
Re: Yet Another Stupid CGI Question
by alien_life_form (Pilgrim) on Oct 19, 2001 at 12:57 UTC
    Greetings.

    Not understanding while you want to interpolate the entire sub, I would offer more ways to do it.
    (1)

    # Trivial double loop - build, then print my $aref=[]; #reference to anonymous array. foreach $thing (@thingies) { push @{$aref},foo($thing); } print_table($aref); #... sub print_table { my $aryref=shift foreach my $thing (@{$aryref}) { #etc. } }

    (2)
    #bypass cgi's html facilities print '<table>'; foreach $thing (@thingies){ #foo returns an array of cell contents. print '<tr><td>',join('</td><td>', foo($thing)),'</td><tr>'; } print '</table>';
    CGI's html convenience functions are - IMHO - just that, convenience (as opposed to the powerful parameter parsing, handling, etc. where CGI unreplaceably shines).
    Sometimes rolling your own html can be just the thing.

    Cheers,
    alf

Re: Yet Another Stupid CGI Question
by netjackal (Acolyte) on Oct 19, 2001 at 14:26 UTC
    Instead of putting the foreach loop in the print statement itself you could always do something like this

    my @array = ( 4,5,3,4,5); print function(),"\n"; sub function { my $rv; foreach my $item (@array) { $rv .= $item . "\t"; } return $rv; }

    and put the foreach loop in a sub (or anon. sub). I know the example above looks silly but i had to work the foreach in to illustrate my point. HTH

Re: Yet Another Stupid CGI Question
by robot_tourist (Hermit) on Oct 19, 2001 at 14:33 UTC
    u could put your html tags in the function, then u wouldn't have to worry about interpolation.
Re: Yet Another Stupid CGI Question
by buckaduck (Chaplain) on Oct 19, 2001 at 16:17 UTC
    You can use the HTML::Table module:
    use CGI ':standard'; use HTML::Table; my $table = new HTML::Table; foreach my $thing (@thingies) { my ($name,$cost,$number) = GetThingInfo($thing); $table->addRow( checkbox('checkboxname'), $name, $cost, $number, textfield('textname'), ); } $table->print;

    buckaduck

Re: Yet Another Stupid CGI Question
by ronzomckelvey (Acolyte) on Oct 24, 2001 at 07:54 UTC
    Hi- I had your same question on tables and such months ago, but I just did it my own way.

    I also use CGI for most of the stuff, but I hardcode the html for tables and other minor crap. I've built a few systems that display lots and lots of data, and I use tables for that.

    Depending on what I'm doing I either handle everyline being displayed as a seperate table, this way the data get displayed as the browser gets it, not when the table is complete.

    For smaller tables, I use one table, with TR for each line of data, but I still so it on my own.

    ronzo

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://119850]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others wandering the Monastery: (7)
As of 2014-09-01 11:41 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    My favorite cookbook is:










    Results (6 votes), past polls