Tweet

Writing a CGI form

In this tutorial you will learn how to write a Perl CGI form. If you have never done any Perl CGI programming before, we suggest you first look at the CGI Tutorial.

Firstly you need to ensure that you have access to a web server (that has Perl installed), that it has cgi enabled, and that you can put your Perl script into the cgi directory.

Starting your script

Like a standard Perl CGI script we use the CGI module to output the HTML:

    #!/usr/bin/perl
    use strict;
    use warnings;
    use CGI;
    use CGI::Carp qw(fatalsToBrowser);

    my $q = new CGI;

    print $q->header;

    print $q->start_html(-title => 'A web form');

Starting the form

To begin a form, you can use the start_form method:

    print $q->start_form;

Without any parameters, the HTML output is:

    <form method="post" action="/cgi-bin/yourprogram.pl" 
          enctype="application/x-www-form-urlencoded">

Using start_form, you can pass in parameters to define the form's properties and behaviour:

    print $q->start_form(
        -name    => 'main_form',
        -method  => 'GET',
        -enctype => &CGI::URL_ENCODED,
        -onsubmit => 'return javascript:validation_function()',
        -action => '/where/your/form/gets/sent', # Defaults to 
                                                 # the current program
    );

The above code would produce the following HTML:

    <form method="get" action="/where/your/form/gets/sent" 
          enctype="application/x-www-form-urlencoded" 
          onsubmit="return javascript:validation_function()" name="main_form">

When you end the form, you can use the end_form method:

    print $q->end_form;

Form Fields

Form field methods are very similar to the standard html tag methods, but tend to require more parameters. The CGI module provides interfaces to more form fields than these, but these are the most common.

Text:

    print $q->textfield(
        -name      => 'text1',
        -value     => 'default value',
        -size      => 20,
        -maxlength => 30,
    );

You can add other parameters that would normally appear in a text field definition (with a '-' in front), such as -onchange, or -class. The above code produces HTML as follows:

    <input type="text" name="text1" value="default value" 
           size="20" maxlength="30" />

Textarea:

    print $q->textarea(
        -name  => 'textarea1',
        -value => 'default value',
        -cols  => 60,
        -rows  => 3,
    );

This produces the following HTML:

    <textarea name="textarea1" rows="3" cols="60">default value
    </textarea>

Hidden:

Hidden fields are very useful in web forms:

    print $q->hidden(
        -name      => 'hidden1',
        -default   => 'some value',
    );

This produces the following HTML:

    <input type="hidden" name="hidden1" value="some value" />

Select:

Select lists are a little more complicated.

To create a simple select list, where all the values are the same as the displayed text, you can do the following:

    my @values = ('value1', 'value2', 'value3');
    print $q->popup_menu(
        -name    => 'popup1',
        -values  => \@values,
        -default => 'value2'
    );

This produces the following HTML:

    <select name="popup1">
    <option value="value1">value1</option>
    <option selected="selected" value="value2">value2</option>
    <option value="value3">value3</option>
    </select>

Or, if the select list is more complex, for example if the displayed text is to be different to the option values, or if you want to add attributes to the options:

    my @values = ('value1', 'value2', 'value3');
    my %labels = (
        'value1' => 'Choice 1',
        'value2' => 'Choice 2',
        'value3' => 'Choice 3',
    );

    print $q->popup_menu(
        -name => 'popup1',
        -values     => \@values,
        -default    => 'value2',
        -labels     => \%labels,
        -attributes => {
            'value1' => {
                'style' => 'color: red'
            }
        },
    );

The HTML this produces is:

    <select name="popup1">
    <option style="color: red" value="value1">Choice 1</option>
    <option selected="selected" value="value2">Choice 2</option>
    <option value="value3">Choice 3</option>
    </select>

Or if you want to do the same, but only have one data structure storing the data (instead of both the @values array and the %labels hash):

    my %labels = (
        'value1' => 'Choice 1',
        'value2' => 'Choice 2',
        'value3' => 'Choice 3',
    );

    print $q->popup_menu(
        -name       => 'popup1',
        -values     => [keys %labels],
        -default    => 'value2',
        -labels     => \%labels,
        -attributes => {'value1' => {'style' => 'color: red'}},
    );

Checkbox:

The following code will create a single checkbox field:

    print $q->checkbox(
        -name    => 'check2',
        -checked => 1,
        -value   => 'two',
        -label   => 'The number two',
    );

This produces the following HTML:

    <input type="checkbox" name="check2" value="two" checked="checked" />The number two

The next code will create a group of checkboxes of the same name:

    print $q->checkbox_group(
        -name     => 'check1',
        -values   => ['one', 'two', 'three', 'four'],
        -defaults => ['one', 'two'],
        -columns  => 2,
        -rows     => 2,
    );

You can affect the layout of the checkboxes by modifying the values of -columns and -rows.

The above code results in the following HTML:

    <table>
    <tr>
    <td><input type="checkbox" name="check1" value="one" checked="checked" />one</td>
    <td><input type="checkbox" name="check1" value="three" />three</td>
    </tr>
    <tr>
    <td><input type="checkbox" name="check1" value="two" checked="checked" />two</td>
    <td><input type="checkbox" name="check1" value="four" />four</td>
    </tr>
    </table>

The table is the result of the -columns and -rows parameters.

Radio:

A radio button group is exactly the same as a checkbox group, except that there can only be one default value:

    print $q->radio_group(
        -name    => 'radio1',
        -values  => ['one', 'two', 'three', 'four'],
        -default => 'one',
        -columns => 2,
        -rows    => 2,
    );

The results in:

    <table>
    <tr>
    <td><input type="radio" name="radio1" value="one" checked="checked" />one</td>
    <td><input type="radio" name="radio1" value="three" />three</td>
    </tr>
    <tr>
    <td><input type="radio" name="radio1" value="two" />two</td>
    <td><input type="radio" name="radio1" value="four" />four</td>
    </tr>
    </table>

Single radio buttons don't make much sense in a web form, so they are not addressed by the CGI module.

Submit:

A submit button is used to submit your data to the URL provided in the 'action' property of the form. Often in cgi scripts you leave the 'action' property blank, so that the form is submitted back to the same program. This means that you only have one file to maintain for a form.

You can output a submit button like this:

    print $q->submit(
        -name     => 'submit_form',
        -value    => 'Click here!',
        -onsubmit => 'javascript: validate_form()',
    );

Button:

A standard button is very similar:

    print $q->button(
        -name     => 'submit_form',
        -value    => 'Click here!',
        -onsubmit => 'javascript: validate_form()',
    );

Except that it will not submit the form (unless the javascript function does that).

Retrieving the Data

When the form is submitted, the web server will run the CGI program specified in the ACTION parameter (or the same program if none is specified). This time, however, the CGI object has the values for all the form fields (or parameters specified in the URL).

You can access these values with the param() method:

    my $text1_value = $q->param('text1');

Most fields return scalar values (e.g. a text field returns a single string), but some (for example, checkbox groups) can return many values. You can access these in the same way:

    my @checked_boxes = $q->param('check1');

Or you can get all the parameters in a hash by using the Vars method:

    my $all_params = $q->Vars;
    foreach my $param (keys %$all_params) {
        print "$param: " . $all_params->{$param} . "<BR>";
    }

If you need all the parameter names, use the param method in array context:

    my @param_names = $q->param();

Program structure

To help you design your programs into nice readable web forms, we suggest the following architecture (pseudo code):

    1. print http header        # $q->header

    2. print html_header_method # To make your pages look the same

    3a. if there are no parameters
        output the form

    3b. else if there is a key parameter # You may include a 
        handle the results of the form   # hidden 'mode' field 
                                         # to make this easier

    4. print end_html_method # May include a standard footer

Of course, you could make your program output many different forms and pages by adding more conditions:

    1. print http header

    2. print html_header_method

    3a. if there are no parameters
        output the first form

    3b. else if there is a key parameter 
        (e.g. if ($q->param('mode') eq 'first_page')
        handle the results of the first form
        output the second form

    3c. else if there is a key parameter 
        (e.g. if ($q->param('mode') eq 'second_page')
        handle the results of the second form
        output the third form

    3d. else if there is a key parameter 
        (e.g. if ($q->param('mode') eq 'third_page')
        handle the results of the third form

    4. print end_html_method # May include a standard footer

A fully worked example

The following is an example of a questionnaire. The user fills in the form and submits it. The results are then displayed on the screen. Realistically, the results would probably be written to a database (using the DBI module, for example).

    #!/usr/bin/perl

    use strict;
    use warnings;
    use CGI;
    use CGI::Carp qw(fatalsToBrowser); # Remove this in production

    my $q = new CGI;

    print $q->header();

    # Output stylesheet, heading etc
    output_top($q);

    if ($q->param()) {
        # Parameters are defined, therefore the form has been submitted
        display_results($q);
    } else {
        # We're here for the first time, display the form
        output_form($q);
    }

    # Output footer and end html
    output_end($q);

    exit 0;

    #-------------------------------------------------------------

    # Outputs the start html tag, stylesheet and heading
    sub output_top {
        my ($q) = @_;
        print $q->start_html(
            -title => 'A Questionaire',
            -bgcolor => 'white',
            -style => {
	        -code => '
                    /* Stylesheet code */
                    body {
                        font-family: verdana, sans-serif;
                    }
                    h2 {
                        color: darkblue;
                        border-bottom: 1pt solid;
                        width: 100%;
                    }
                    div {
                        text-align: right;
                        color: steelblue;
                        border-top: darkblue 1pt solid;
                        margin-top: 4pt;
                    }
                    th {
                        text-align: right;
                        padding: 2pt;
                        vertical-align: top;
                    }
                    td {
                        padding: 2pt;
                        vertical-align: top;
                    }
                    /* End Stylesheet code */
                ',
	    },
        );
        print $q->h2("A Questionaire");
    }

    # Outputs a footer line and end html tags
    sub output_end {
        my ($q) = @_;
        print $q->div("My Web Form");
        print $q->end_html;
    }

    # Displays the results of the form
    sub display_results {
        my ($q) = @_;

        my $username = $q->param('user_name');
        my $userage = $q->param('age');
        my $usergender = $q->param('gender');
        my @favourite_languages = sort $q->param('language');
        my %sex = ('F' => 'girl', 'M' => 'boy');

        print $q->h4("Hi $username");
        print $q->p("You are a $sex{$usergender}, and you are $userage years old.");
        print $q->p("Your favourite languages are:");

        print $q->table(
            {-border => 1, -cellpadding => 3},
            $q->Tr($q->td(\@favourite_languages)),
        );
    }

    # Outputs a web form
    sub output_form {
        my ($q) = @_;
        print $q->start_form(
            -name => 'main',
            -method => 'POST',
        );

        print $q->start_table;
        print $q->Tr(
          $q->td('Name:'),
          $q->td(
            $q->textfield(-name => "user_name", -size => 50)
          )
        );
        print $q->Tr(
          $q->td('Age:'),
          $q->td(
            $q->radio_group(
              -name => 'age',
              -values => [
                  '0-12', '13-18', '18-30', '30-40', '40-50', '50-60', '60-70', '70+'
              ],
              -rows => 4,
            )
          )
        );
        my %genders = ('F' => 'Female', 'M' => 'Male');
        print $q->Tr(
          $q->td('Gender:'),
          $q->td(
            $q->popup_menu(
              -name => 'gender',
              -values => [keys %genders],
              -labels => \%genders,
            )
          )
        );
        print $q->Tr(
          $q->td('Favourite Languages:'),
          $q->td(
            $q->checkbox_group(
              -name => 'language',
              -values => ['Perl', 'C', 'C++', 'C#', 'Java', 'VB', 'Python', 'Delphi'],
              -defaults => ['Perl'],
              -columns => 2,
            )
          )
        );
        print $q->Tr(
          $q->td($q->submit(-value => 'Submit')),
          $q->td('&nbsp;')
        );
        print $q->end_table;
        print $q->end_form;
    }

See also

    perldoc CGI
[Top]