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:

  • filters - e.g. remove leading/trailing spaces
  • constraints - low-level checks ("is this a number?", "is this a valid e-mail address?")
  • inflators - turn a value into an object, ready to be passed to validators
  • validators - application-level checks ("is this username unique?")

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

  • Inherit from FormFu

    use parent 'Catalyst::Controller::HTML::FormFu';
    
  • Create an action to handle GET and POST for the form. By adding the FormConfig attribute to an action, you'll get a FormFu object in your stash. Without arguments, FormConfig will look for the form configuration file in root/forms/<controller_private_path>/<action>.yml (e.g. /root/forms/person/add for Controller::Person::add, or /root/forms/index for Controller::Root::index). This is similar to not specifying a template.

    sub input_formfu :Local :FormConfig {
        my ( $self, $c ) = @_;
        ...
    
  • Get the form that the attribute FormConfig stashed

    my $form = $c->stash->{form};
    
  • Test if the form has been submitted and validates

    if ( $form->submitted_and_valid ) {
        # act on the form data, available in $c->req->param()
        # 
    }
    
  • Test if the form has errors

    if ( $form->has_errors) {
        # Let FormFu do the work of highlighting fields with errors
        # and tell you a bit about the error type
    }
    
  • Test if the form has not been submitted

    if ( !$form->submitted ) {
        # show the form
    }
    

NOTES:

  • If you want a YAML file extension of .yaml (instead of .yml), you have to configure that. One way is by putting the following in our myapp.yaml configuration file:

    'Controller::HTML::FormFu': { config_file_ext: .yaml }
    
  • Full Example, the verbose way (without the :FormConfig attribute).

Resources

My tags:
 
Popular tags:
 
Powered by Catalyst
Powered by MojoMojo Hosted by Shadowcat - Managed by Nordaaker