Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

Generalising Forms from CGI.pm

by Cody Pendant (Prior)
on Feb 19, 2003 at 01:43 UTC ( [id://236488]=perlquestion: print w/replies, xml ) Need Help??

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

I'm trying to make a generalised form-creation script, with a certain look and feel, that I can re-use for various things, using CGI.pm.

Here's an example:

%fields = ( 'name', 'textfield', 'email', 'textfield', 'site name', 'textfield', 'description', 'textfield', 'comments', 'textarea' ); foreach $key (keys(%fields)) { $thisrow = td({-align=>right,-valign=>TOP},$key); if($fields{$key} eq 'textfield'){ $thisrow .= td({-align=>left,-valign=>TOP}, textfield(-name=>$key,-size=>50,-maxlength=>80) ); } if($fields{$key} eq 'textarea'){ $thisrow .= td({-align=>left,-valign=>TOP}, textarea(-name=>$key,-cols=>50,-rows=>10) ); } push(@rows,$thisrow); } print table({-border=>1,-width=>'50%'}, Tr(\@rows) );
But as you can see, it's not going to be very generalised, because I'm having to test for the type of field every time: if it's a textfield, do the textfield thing, if it's a textarea, do something else.

What I can't figure out is how to do this:

$thisrow .= td({-align=>left,-valign=>TOP}, $fields{$key}(-name=>$key)); # make a textfield # because the value is # 'textfield' - DWIM!
i.e. call the particular CGI.pm sub using the value, which is the name of that sub.

Any help greatly appreciated...
--

“Every bit of code is either naturally related to the problem at hand, or else it's an accidental side effect of the fact that you happened to solve the problem using a digital computer.”
M-J D

Replies are listed 'Best First'.
(jeffa) Re: Generalising Forms from CGI.pm
by jeffa (Bishop) on Feb 19, 2003 at 02:01 UTC
    Hmmmm ... personally, i would use a templating system for this kind of problem, but ... if you don't mind the perfomance hit, you could point each key in your hash to an anonymous subroutine that takes a single arg and calls the proper CGI subroutine:
    my $thisrow; my @rows; my %fields = ( name => sub { textfield(-name=>$_[0],-size=>50,-maxlength=>80 +) }, email => sub { textfield(-name=>$_[0],-size=>50,-maxlength=>80 +) }, site_name => sub { textfield(-name=>$_[0],-size=>50,-maxlength=>80 +) }, descripton => sub { textfield(-name=>$_[0],-size=>50,-maxlength=>80 +) }, comments => sub { textarea(-name=>$_[0],-cols=>50,-rows=>10) }, ); foreach my $key (keys %fields) { $thisrow = td({-align=>'right',-valign=>'TOP'},$key) . td({-align=>'left',-valign=>'TOP'}, $fields{$key}->($key)) ; push(@rows,$thisrow); }
    However, i would never use that in any of my code. The way i see it, using CGI.pm HTML generation methods IS my abstraction. Wrapping that into something else seems a bit silly. Have you tried HTML::Template yet? You might find that it is all you need. ;)

    p.s. don't forget that hashes are not ordered - you might want to try a list of hashes instead

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    

      I can't believe no one has mentioned CGI::Formbuilder yet:

      I believe that Nathan Wiger has scratched this itch particularly well, making it very straight forward to do complicated things with CGI forms very simply.

      For posperity here is the SYNOPSYS from the documentation

      use CGI::FormBuilder; # Let's assume we did a DBI query to get existing values my $dbval = $sth->fetchrow_hashref; my $form = CGI::FormBuilder->new( method => 'POST', fields => [qw/name email phone gender/], values => $dbval, validate => { email => 'EMAIL', phone => 'PHONE' } +, required => 'ALL', font => 'arial,helvetica', ); # Change gender field to have options $form->field(name => 'gender', options => [qw/Male Female/]); if ($form->submitted && $form->validate) { my $fields = $form->field; # get form fields as hashref # Do something to update your data (you would write this) do_data_update($fields->{name}, $fields->{email}, $fields->{phone}, $fields->{gender}); # Show confirmation screen print $form->confirm(header => 1); # Email the person a brief confirmation $form->mailconfirm(to => $fields->{email}); } else { # Print out the form print $form->render(header => 1); }
      --
      Clayton aka "Tex"
Re: Generalising Forms from CGI.pm
by tachyon (Chancellor) on Feb 19, 2003 at 02:02 UTC
Re: Generalising Forms from CGI.pm
by pfaut (Priest) on Feb 19, 2003 at 02:28 UTC

    Your use of a hash to define your fields leaves a bit of a dilemma: you never know what order they will be in when you invoke keys. This means you have given perl control over the order the fields appear in the form. You'd be better off using an array.

    Instead of storing the field type, store a code reference to a subroutine that can generate that type of tag. The sub can take arguments to control creation of the input field.

    use CGI qw(:standard); sub TEXTFIELD { my ($name,$siz,$max)=@_; $siz ||= 50; $max ||= 80; return textfield(-name=>$name,-size=>$siz,-maxlength=>$max); } sub TEXTAREA { ...generate a textarea... }; sub SUBMIT { ...generate a submit button... }; my @fields = ( { type=>\&TEXTFIELD, args=>['name'] }, { type=>\&TEXTFIELD, args=>['email'] }, { type=>\&TEXTFIELD, args=>['site name'] }, { type=>\&TEXTFIELD, args=>['description'] }, { type=>\TEXTAREA, args=>['comments'] }, ); my @rows; for (@fields) { my $control = $_->{type}->(@{$_->{args}}); push @rows,td({-align=>left,-valign=>TOP},$control); }
    --- print map { my ($m)=1<<hex($_)&11?' ':''; $m.=substr('AHJPacehklnorstu',hex($_),1) } split //,'2fde0abe76c36c914586c';
Re: Generalising Forms from CGI.pm
by Cody Pendant (Prior) on Feb 19, 2003 at 06:11 UTC
    Thank you all for your help. I think I'm rapidly realising I've become one of those "please help me use my hammer on this screw" people...
    --
    “Every bit of code is either naturally related to the problem at hand, or else it's an accidental side effect of the fact that you happened to solve the problem using a digital computer.”
    M-J D
•Re: Generalising Forms from CGI.pm
by merlyn (Sage) on Feb 19, 2003 at 14:16 UTC
      Thanks merlyn, that's just the kind of thing I was aiming for (though I don't need more than one page).

      Just for the benefit of other monks, merlyn's column includes this:

      sub DEFAULT_FIELD { textfield(shift, "", 60) }
      as the way fields are produced unless specifically over-ridden, and the tables are produced like this:
      print table({ Border => 1, Cellpadding => 5 }, map { Tr( th($_->[1]), td(($_->[2] || \&DEFAULT_FIELD)->($_->[0])) )} @info );
      where the details of the form are in the @info array.

      Update: you know what's occurring to me? The details of the form (arrays of arrays for the most part) could easily be stored in XML -- I'm sure if the column were written today that might have come up.
      --

      “Every bit of code is either naturally related to the problem at hand, or else it's an accidental side effect of the fact that you happened to solve the problem using a digital computer.”
      M-J D

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://236488]
Approved by Coruscate
Front-paged by Coruscate
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others avoiding work at the Monastery: (4)
As of 2024-03-19 04:16 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found