Dynamic Forms with HTML::FormFu

Recently, we had to rework a legacy project using Catalyst. We chose HTML::FormFu as our Form engine because we had good experience with it in other projects.

However, we had to face one problem: Forms had to be different for every user. Well, the difference is based on the user's roles, to be precise.

As a rescue and a generic approach, we chose to create a simple HTML::FormFu::Plugin that can get applied to every or some of the forms, just as needed. To get things working, we need to do two things:

  • mark certain fields inside the form with the rights we need for editing
  • have our plugin make the privileged fields readonly

A simple form (in perl syntax) might look like this:

{
    auto_fieldset => 0,
    elements => [
        {
            type => 'Text',
            label => 'Artno',
            name => 'artno',
            # require admin rights to edit artno
            stash => { rights => 'admin' },
            # also possible for marking fields (but visible in the HTML Markup): 
            attributes => { 'data-right' => 'admin' },
        },
        {
            type => 'Text',
            label => 'Name',
            name => 'name',
        },
    ],
    plugin => 'FixFields',
}

And the plugin is a package HTML::FormFu::Plugin::FixFields looking like that:

package HTML::FormFu::Plugin::FixFields;
use Moose;
extends 'HTML::FormFu::Plugin';

sub process {
my $self = shift;

my $form = $self->form;

foreach my $field (@{$form->get_all_elements}) {
next if !$field->{stash};
next if !$field->{stash}->{rights};

if ($field->{stash}->{rights} eq 'admin') {
# only editable for admin, just hide to demonstrate
$field->attributes->{readonly} = 1;
}
}
}

1;

As a prove of concept, we just hide every field that is marked with stash => { rights => 'somename' }. Every other logic at this place is possible as well.

Conclusion: keeping your controllers slim by making forms intelligent is possible and simple.

PS: yes, repeating things is ugly. Marking every form with plugin => 'FixFields' is bad. Moving this entry to a global config file is also possible. Just add a constructor entry to your config file like this (again in perl format):

    'Controller::HTML::FormFu' => {
        constructor => {
            plugins => [ 'FixFields' ],
        },
    },

1 Comment

Great article! Never even knew this module existed -- but now I may use it on an upcoming project.

Leave a comment

About Wolfgang Kinkeldei

user-pic I blog about Perl.