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' ],
},
},
Great article! Never even knew this module existed -- but now I may use it on an upcoming project.