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

Dear Monks

I'm writing a CGI script in which each radio button may have sub-options (i.e. textfields, checkboxes), each of which is defined in a hash:

my %options = ( show_reqs => checkbox(-name=>'show_reqids', -checke +d=>0, -value=>'ON', -label=>'Show requirement IDs?'), date => "<b>Created: </b>" . radio_group(-name=>'bef_sin +ce', -values=>['before', 'since'], -default=>'none', -labels=>{'before' => 'before ', 'since' => 'since ( +inclusive)'}) . "&nbsp;&nbsp;<b>Date</b> (YYYY-MM-DD): " . textfield(-name=>'date', -size=>10, -maxlength=>10), req_type => "<b>Requirement Type</b> (e.g. BRQ): " . textfiel +d(-name=>'reqtype', -size=>5, -maxlength=>5), );

The names and options used for a particular radio-button are defined in another hash under the "opts" field:

my %report_types = ( 0 => {name => "Report One", opts => ['show_reqs', 'date'], coderef => \&report_typ +e_two}, 1 => {name => "Report Two", opts => ['show_reqs', 'req_type', 'date'], coderef => +\&report_type_three}, );
Later on in the script, I loop over the keys of %report_types to generate the sub-options appropriate for each report type.

The problem is that when several textfields have the same name, they can step on each other when I try to access them. I was thinking that if I could assign a unique value to the -name field for each repeated sub-option, then I could avoid this. For example, report choices will be based on the keys of %report_types ("0" and "1"), and each will have a get a sub-option of "date" that contains a "date" textfield. The names I would like to give to these textfields are "date_0" and "date_1".

Is there any way to do this at runtime? I've been thinking that a codref might work, but I can't figure out how. Any ideas? Or is there a better approach altogether?

Update: I have put a standalone testcase in the "readmore" section below:

#!c:/perl/bin/perl.exe -T use CGI qw(:standard); use CGI::Carp qw(warningsToBrowser fatalsToBrowser); use strict; use warnings; my $TITLE = "Reporting Utility"; # Options available for each report type my %options = ( show_reqs => checkbox(-name=>'show_reqids', -che +cked=>0, -value=>'ON', -label=>'Show requirement IDs?'), date => "<b>Created: </b>" . radio_group(-name=>'bef_si +nce', -values=>['before', 'since'], -default=>'none', -labels=>{'before' => 'before ', 'since' => 'sin +ce (inclusive)'}) . "&nbsp;&nbsp;<b>Date</b> (YYYY-MM-DD): " . textfield(-name=>'date', -size=>10, -maxlength=>10 +), req_type => "<b>Requirement Type</b> (e.g. BRQ): " . textfi +eld(-name=>'reqtype', -size=>5, -maxlength=>5), ); my %report_types = ( 0 => {name => "Project Requirements: Filtered +by Creation Date", opts => ['show_reqs', 'date'], coderef => \&report +_type_two}, 1 => {name => "Project Requirement Revisions: Filtered by +Creation Date, Type, and Version Reason", opts => ['show_reqs', 'req_ +type', 'date'], coderef => \&report_type_three}, ); # Hash for use with populating below radio_group my %radio_hash; foreach my $type (keys %report_types) { $radio_hash{$type} = $report_types{$type}{'name'}; } # Used to create the radio group of report types my @rep_type_rad_gr = radio_group(-name=>'report_type', -values=>[sort + {$a <=> $b} keys %radio_hash], -default=>'none', -labels=>\%radio_ha +sh); print header, start_html(-title=>$TITLE, -bgcolor=>"#ffcc99"), h2($TIT +LE), hr; my $query = CGI->new(); if (defined $query->param('report_type')) { # If the user has made a s +election my $show_reqids = "false"; if ($query->param('show_reqids') eq "ON") { $show_reqids = "true"; } print("<blockquote>\n"); print h3("$report_types{$query->param('report_type')}->{'name'}") +, "\n"; # Run report based on user selection, including all $query->param + values as arguments $report_types{$query->param('report_type')}{'coderef'}->($show_re +qids, $query->param('bef_since'), $query->param('date'), $query->para +m('reqtype'), $query->param('ver_reason')); $query->delete_all; print start_form; print defaults("Start over"); print end_form; print("</blockquote>\n"); } else { # User has not made a selection print("<blockquote>\n"); print p("<h3>Choose the type of report:</h3>\n"); print start_form; foreach my $i (0..$#rep_type_rad_gr) { # For each radio button print "<p><b>", $rep_type_rad_gr[$i], "</b>"; foreach my $option ( @{$report_types{$i}->{opts}} ) { # Print +the options associated with a given report type print "<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;", $options +{$option}, "\n"; } print "</p>\n"; } print p(submit("Submit")); print end_form; print("</blockquote>\n"); } print end_html;
Thank you,
memnoch

Replies are listed 'Best First'.
Re: Is there a way to set a textfield -name value at runtime?
by eric256 (Parson) on Jan 25, 2008 at 21:55 UTC

    I'm having a bit of a hard time understanding how the two code blocks you show are related. I think you want fields to have a unique name even when more then one might be on the page at the same time. In the past if i need multiple fields with the same name for different records i prepend the record id. So you might have report_0_date and report_1_date, I'm not convinced this is what you are after. Maybe if you made a complete small scale example?


    ___________
    Eric Hodges

      Thanks Eric...I have updated my posting with a standalone test case of what I'm trying to do. The two blocks of code in the main posting relate to each other like this:

      - The %options hash has a set of cgi objects (i.e. checkboxes, textfields, radio buttons) that I want to be able to select from as sub-options for a given top-level radio button.

      - Each key of %report_types points to a hashref that defines the properties of that particular report: its name (name), its sub-options (opts), and its generator (coderef).

      Since there are two reports defined, each of which has a "date" textfield as a sub-option, I'd like to name these textfields according to the report type (i.e. "date_1" and "date_2"). But since the "date" textfield is defined once in the %options hash, I don't see how to do this.

      Update: I just realized that I can probably do what I want by making my own Reports class, creating instances at runtime with the necessary sub-options. The names of the textfields can then be assigned unique values by passing in an appropriate parameter during the instance construction.

        A little formating goes a long way for clarity here:

        my %options = ( show_reqs => checkbox( -name => 'show_reqids', -checked => 0, -value => 'ON', -label => 'Show requirement + IDs?'), date => "<b>Created: </b>" . radio_group( -name=>'bef_since', -values=>['before', 'since'], -default=>'none', -labels => {'before' => 'b +efore ', 'since' => 's +ince (inclusive)'} ) . "&nbsp;&nbsp;<b>Date</b> (YYYY-MM-DD +): " . textfield(-name=>'date', -size=>10, +-maxlength=>10), req_type => "<b>Requirement Type</b> (e.g. BRQ): " . textfield( -name => 'reqtype', -size => 5, -maxlength=>5), ); my %report_types = ( 0 => { name => "Project Requirements: Filtered by +Creation Date", opts => ['show_reqs', 'date'], coderef => \&report_type_two }, 1 => { name => "Project Requirement Revisions: Fil +tered by Creation Date, Type, and Version Reason", opts => ['show_reqs', 'req_type', 'date'], coderef => \&report_type_three}, );

        It is now more obvious that you are generating the actual elements a little soon. I would make the options sub refs that take the report name as an argument (and possibly more) so that you could create unique instances. At least they should take the fields own name as an argument. I think the following will be close:

        my %options = ( show_reqs => sub { my $report = shift; return checkbox( -name => $report . '_show_ +reqids', -checked => 0, -value => 'ON', -label => 'Show requirement + IDs?'), } ); print $options{$option}->($report), "\n";

        In this manner your options are constructors. They could even take in more arguments, like default values or something, so that each report could have different defaults.


        ___________
        Eric Hodges