[Catalyst] Rails-like form helpers

Bill Moseley moseley at hank.org
Thu Sep 21 14:46:13 CEST 2006

On Thu, Sep 21, 2006 at 10:25:37AM +0100, Jon Warbrick wrote:
> person, telephone_number and email_address have multiple columns. person 
> has_many telephone_numbers, person has_many email_addresses. I want a form 
> to manage information about a particular person, including the ability to 
> update existing telephone numbers and email addresses, and with buttons to 
> delete any particular number and to add new ones. I don't think HTMLWidget 
> can do that (though I'd be happy to be proved wrong).

The form code I use handles this -- by looking at the relationships of
the object its updating.  One-to-many relationships end up as
drop-downs or radio selects (depending on the size of the option list)
and many-to-many are either checkbox groups or multi-select drop-

I can handle multi-widget fields (area code + prefix + number or date
+ time) but I don't use those -- I just uses simple text input fields
and parse and validate.  (It also drives me crazy when I see
instructions on fields "Enter credit card number without the dashes".)

You should check out Rose::HTML::Objects if you have not already.

> There is also the issue that a complex form it may need careful manual 
> layout of the various controls to make it usable. As I see it this is 
> something for the designer to do, working in the relevant templates, 

Yes, my forms code doesn't do any layout of the fields.  I do that
manually.  I always seem to need to hand tweak the layout of the

Also, my form code doesn't generate the actual html -- again, that's
done in a separate template.  Most of the formatting is with css, but
I can use a different form template if I want to change the field
generation in ways the css can't handle.

My forms are all (well almost all) separate modules.  So my
controllers looks like this:

    sub edit : Local {
        my ( $self, $c, $id ) = @_;

        $c->stash->{form} = WS2::Form::Admin::Organization->new( $id );

        # Now validate
        $c->stash->{form}->update_from_form( $c->req->parameters )
            if $c->form_posted;

With the form module:

    package WS2::Form::Admin::Organization;
    use strict;
    use warnings;
    use base 'Form::Model::CDBI';

    # Define the object class that this form handles
    sub object_class { 'DB::Organization' }

    sub profile {
        my ( $self ) = @_;

        return {
            required => {
                name                    => 'Text',
                active                  => 'Boolean',
                organization_type       => 'Select',
                groups                  => 'Multiple',

            optional => {
                parent_organization     => 'Select',
                contact                 => 'Text',
                email                   => 'Email',
                phone                   => 'Phone',
                address1                => 'Text',
                address2                => 'Text',
                city                    => 'Text',
                state                   => 'Select',
                zip                     => 'USZipcode',
                url                     => 'URL',
                comment                 => 'TextArea',
            dependency => [
                [qw/ address1 city state zip /],

    # extra validation can be here:

    sub validate_foo {
        my ( $form, $field ) = @_;

        $field->add_error('Foo must be "bar"')
            unless $field->value eq 'bar';


Then finally, the form that handles the above would looks something

[% # Form to edit an oranization

    item = form.item;

    page.title = 'Organization: ' _ (item ? item.name : 'New Entry');

    page_link( 'list', 'list all organizations' );

    WRAPPER form_wrapper; PROCESS fields; END;


[% BLOCK fields;

    "<fieldset><legend>Organization Information</legend>";
        field('Organization Name', 'name');
        field('Organization Type', 'organization_type' );
        field('Organization Active', 'active');
        field('Member Of Groups', 'groups' );

        field('Parent Organization', 'parent_organization' );

        field('Contact Person', 'contact');
        field('Phone', 'phone',
            'Please enter the number to reach the contact');

        field('Email', 'email');
        field('URL', 'url');

    "<fieldset><legend>Mailing Address</legend>";
        field('Address line 1', 'address1' );
        field('Address line 2', 'address2' );
        city_state_zip();  # does ajax updated zip-lookup.

END %]

Bill Moseley
moseley at hank.org

More information about the Catalyst mailing list