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.