HTML::FormFu

Use HTML::FormFu within Catalyst to take the tedium out of building, validating and error handling forms. Before proceeding with the rest of this tutorial, make sure you have read the introduction to forms.

How FormFu works

To have FormFu work with Catalyst, use Catalyst::Controller::HTML::FormFu. FormFu can construct a form's HTML representation either programmatically, or from a form definition file. The resulting object is placed on the stash, in $c->stash->{form}, and it's good practice to fetch it into a $form variable. It has methods for managing form logic and validation, changing the default behavior, getting/setting form attributes (including CSS classes and localization), processing a form, error handling, field (parameter) retrieval, rendering the form, advanced customization and introspection.

If the form was submitted and valid, parameters will be available via $form->param_value('field_name') (and also $c->request->parameters->{field_name}, but see FormFu issue 56).

Creating a form

Most often, you'll want to define forms in the root/forms directory of your application, and have FormFu construct them automatically per controller action, then render them in your template files and make small tweaks to the fields. Here is what a simple login form may look like:

action: /login
# indicator is the field that is used to test for form submission
indicator: submit

# Start listing the form elements
elements:
    # The first element will be a text field for the username
    - type: Text
      name: user
      label: Username
      constraints:
          - Required

    # The password field
    - type: Password
      name: pass
      label: Password
      constraints:
          - Required

    # The Submit button
    - type: Submit
      name: submit
      # The text to be displayed
      value: Login

This form definition file was written in YAML, but any config format supported by Config::Any will work. This can include XML, YAML, JSON, Apache-style configuration, Windows INI files, and even Perl code.

Note that most of the field names in the form are simply HTML element names (or attributes).

To construct that form at runtime, simply add the :FormConfig attribute to your action, and to render it, add [% form %] somewhere in your template. Having the :FormConfig attribute will instruct FormFu to put the form object in the stash. Once you fetch the form object (my $form = $c->stash->{form}), you can manipulate its elements with method calls on it. Remember to call $form->process after any programmatic form manipulation (more). Also, $form->process should be called only once per request{{fact}}.

Here's a quick example that changes the label of the user field and prefills it with some default username:

sub index :Path :Args(0) :FormConfig {
    my ( $self, $c ) = @_;

    my $form = $c->stash->{form};

    my $form_elem_username = $form->get_field({name => 'user'});
    $form_elem_useranme->label('Who are you?');

    # pre-fill the username field
    $form_elem_username->value('admin');

    $form->process;
}

As you can see, HTML elements (label) and attributes (value) are mapped easily by FormFu. For example, Labels are automatically bound to their elements - if you look at the generated HTML source, you'll see label for tags. get_field and similar methods will return a HTML::FormFu::Element, on which you can call ->value, ->label etc.

Form validation

Form field input goes through 4 stages, in order, and as long as the result of each stage is successful:

FormFu and DBIC

Let's assume a usual Catalyst application, using Template-Toolkit and DBIC, which now needs forms. This is how FormFu gets bolted in:

  1. To interface FormFu with DBIC, use HTML::FormFu::Model::DBIC and place the DBIx::Class schema in the form's stash. For example, you can specify the model in your application config file:

schema = ModelName where ModelName is the name of your model that you'd pass to $c->model().

  1. To create a record to be populated with form data,

    if ( $form->submitted_and_valid ) {
        my $book = $c->model('DB::Book')->new_result({});
        # update dbic row with submitted values from form
        $form->model->update( $book );
        $c->response->redirect( $c->uri_for('list') );
        return;
    }
    

Using FormFu - in your controller class

NOTES:

Resources