http://qs1969.pair.com?node_id=274534

Item Description: Automagically populate HTML forms with CGI parameters

Review Synopsis:

After some discussion with grantm, it was suggested that I take a look at HTML::FillInForm because "it's magic". Having used it now, I agree!

HTML::FillInForm takes the form you give it and automagically populates fields on that form with parameters from a CGI object that you also pass it (read: sticky forms). You may be thinking, "I already can do sticky forms with CGI.pm! Why do I need this?" Templating!

While you can use HTML::FillInForm in most any instance (as a substitute for sticky forms with CGI.pm, for example), I have found it to be most invaluable when working with HTML::Template (I imagine it would provide the same benefits for users of TT or HTML::Mason too). It has drastically cut down the complexity and programming required to redisplay a form to a user with their information filled in.

How does it work? First we create the fill-in-form object:

my $form = new HTML::FillInForm; my $page = $form->fill(scalarref => \$html, fobject => $request);
$html contains the HTML of the form to fill in. $request is a CGI object that contains one or more parameters that you would like to fill in your form with. The call to fill() does the rest. It's that easy! fill() returns the HTML of the filled-in form. So how does this save work? Consider the following sample template:
<form method="post" action=<!-- TMPL_VAR NAME=action --> enctype="appl +ication/x-www-form-urlencoded"> <center> <table> <tr> <td valign="top">Pay by </td> <td valign="top"> <input type="radio" name="searchby" value="Parcel" + checked="checked" />Parcel <input type="radio" name="searchby" value="Name" / +>Name <input type="radio" name="searchby" value="Address +" />Address </td> </tr> <tr> <td>Enter Search Value</td> <td><input type="text" name="searchkey" value=<!-- TMP +L_VAR NAME=DEFAULT --> maxlength="255" length="50" /></td> </tr> </table> <input type="submit" name="action" value="Search" /> <input type="submit" name="action" value="Verify" /> <input type="submit" name=".defaults" value="Defaults" /> </center> <input type="hidden" name=".cgifields" value="searchby" /> </form>
The Perl to populate this looks something like:
$tmpl_form->param( ACTION => "/path/to/my/script.cgi", DEFAULT => $request->param("searchkey"), );
Essentially, I have to create an extra TMPL_VAR for each form field that I would like to fill in. With HTML::FillInForm, we can omit the DEFAULT TMPL_VAR and let fill() handle it for us. Not a big deal when dealing with a text field or two, I realize. But what about those radio buttons? Let's adjust our template to account for those:
<form method="post" action=<!-- TMPL_VAR NAME=action --> enctype="appl +ication/x-www-form-urlencoded"> <center> <table> <tr> <td valign="top">Pay by </td> <td valign="top"> <input type="radio" name="searchby" value="Parcel" + <!-- TMPL_IF NAME=PARCEL -->checked="checked"<!-- /TMPL_IF --> />Par +cel <input type="radio" name="searchby" value="Name" < +!-- TMPL_IF NAME=NAME -->checked="checked"<!-- /TMPL_IF --> />Name <input type="radio" name="searchby" value="Address +" <!-- TMPL_IF NAME=ADDRESS -->checked="checked"<!-- /TMPL_IF --> />A +ddress </td> </tr> <tr> <td>Enter Search Value</td> <td><input type="text" name="searchkey" value=<!-- TMP +L_VAR NAME=DEFAULT --> maxlength="255" length="50" /></td> </tr> </table> <input type="submit" name="action" value="Search" /> <input type="submit" name="action" value="Verify" /> <input type="submit" name=".defaults" value="Defaults" /> </center> <input type="hidden" name=".cgifields" value="searchby" /> </form>
Our Perl transforms into something vastly less elegant:
$tmpl_form->param( ACTION => "/path/to/my/script.cgi", DEFAULT => $request->param("searchkey"), PARCEL => $request->param("searchby") eq "Parcel" ? "true" : "" +, NAME => $request->param("searchby") eq "Name" ? "true" : "" +, ADDRESS => $request->param("searchby") eq "Address" ? "true" : "" +, );
What a lot of work to do when we can simply:
$tmpl_form->param( ACTION => "/path/to/my/script.cgi" ); my $html = $tmpl_form->output; my $form = new HTML::FillInForm; $html = $form->fill(scalarref => \$html, fobject => $request);
and be done with it :) And this is just a simple example. Add more complexity to the form and your Perl will grow more complex to account for it. With HTML::FillInForm, you can add complexity to the above form and not have to add any additional code to handle it.

For sake of brevity, I did not include code to validate or detaint parameters here. It goes without saying that you should always be doing those things when processing user input.

One potential downside (note that I don't have benchmarks to back me up) is that it relies upon HTML::Parser to perform its magic. While there's nothing wrong with that, there may be some overhead associated with creating an HTML::Parser object, especially on a high-traffic site (see this node from Ovid). I would expect this to be a rather moot issue on a site running mod_perl.

All in all, an excellent module, and a worthwhile addition to any Perl programmer's bag-of-tricks.

MrCromeDome

Replies are listed 'Best First'.
Re: HTML::FillInForm
by knowmad (Monk) on Aug 14, 2003 at 12:00 UTC

    Nice examples of the benefits of HTML::FillInForm. I avoided it for years because it seemed like yet another module to kee up with; it's now a priority.

    At any rate, I was trying to figure out how to get it to repopulate checkbox groups with multiple values. For some reason, you must use the fdat parameter and pass in a hash reference instead of doing the fobject parameter.

    For example, to process the following code:

    <input value="1" type="checkbox" checked name="need_id"> Advocacy <input value="2" type="checkbox" checked name="need_id"> Domestic Viol +ence <input value="3" type="checkbox" checked name="need_id">Racial Diversi +ty

    You would use something like the following:

    my %fdat = $q->Vars; my $html = <output from favorite flavor of templating language> my $fif = new HTML::FillInForm; $html = $fif->fill(scalarref => \$html, fdat => \%fdat);

    Now sit back and let this module do even more of the busy work for you!

    William
Re: HTML::FillInForm
by bradcathey (Prior) on May 18, 2005 at 13:19 UTC

    Practical application of HTML::FillInForm, used in conjunction with my favorite templating module, HTML::Template, is not well documented anywhere that I could find. Here's my take.

    My biggest use of HTML::FillInForm will be for re-populating, selecting, or checking all the input types in my forms when returning the form for the user to correct a validation error, or when calling up a DB record to be edited. The associate function in H::T only "remembers" text-type input, so to avoid lots of convoluted javascript and Perl, I found the ideal tool in HTML::FillInForm. I did consider using CGI's very own popup_menu feature, but I am trying to keep my HTML completely separate from my Perl.

    Anyway, here are some methods I've discovered in experimenting with the two modules:

    PERL: my $q = new CGI; my $fvalues = $q -> Vars; #get all the value pairs from the submitted + form my $name = "Mr.". $q->param('name'); #alter one of them $fvalues->{name} = $name; my $template = HTML::Template->new( filename => "../form.tmpl"); my $html = $template->output; my $form = new HTML::FillInForm; my $page = $form->fill(scalarref => \$html, fdat => $fvalues); print "Content-type: text/html\n\n"; print $page; HTML: <form action="./fill.pl" method="post"> <input type="text" name="name" size="30" value="<tmpl_var name>" /> +<br /> <input type="text" name="address" size="30" /><br /> <select name="choices"> <option value="">Select...</option> <option value="1">Ciara</option> <option value="2">Lauren</option> <option value="3">Brian</option> </select><br /> <input type="submit"> </form>

    Note: a value passed in the hash ref to fdat trumps assigning a value to a <tmpl_var> of the same name . So, this will not work:

    my $fvalues = $q -> Vars; my $name = "Mr.". $q->param('name'); #alter one of them my $template = HTML::Template->new( filename => "../form.tmpl"); $template->param( name = $name ); my $html = $template->output;

    However, this will:

    my $fvalues = { address => '123 Main Street', choice => '2' }; #BTW, if the form tag is select with multiple attribute, use a referen +ce to an array of the values: #my @choices = (1, 3); #$fifvalues->{'choices'} = \@choices; my $name = "Mr.". $q->param('name'); #alter one of them my $template = HTML::Template->new( filename => "../form.tmpl"); $template->param( name = $name ); my $html = $template->output;

    The above example can be used to populate a new form, e.g., when pulling up a DB record for editing.


    —Brad
    "The important work of moving the world forward does not wait to be done by perfect men." George Eliot