Dancer::Plugin::DataFu - Dancer HTML Form and Grid/Table engine with Input Validation
version 1.103070
# form rendering and validation get 'login' => sub { return form->render('form_name', '/action', 'profile.field', 'profile.field'); # return form->render('login', '/submit_login', 'user.login', 'user.password'); }; post 'login' => sub { my $input = form; return redirect '/dashboard' if $input->validate('user.login', 'user.password'); redirect '/login'; }; # grid rendering # Important Note! The order arguments are received by the render function # has now changed. Please examine. get '/user_list' => sub { return grid->render('table_name', 'profile_name', $dataset); # $dataset is an array of hashes }; # grid rendering with Dancer::Plugin::DBIC get '/user_list' => sub { my $rs = schema->resultset('Foo'); $rs->result_class('DBIx::Class::ResultClass::HashRefInflator'); return grid->render('table_name', 'profile_name', [$rs->all]); };
Dancer::Plugin::DataFu is an HTML form and table rendering engine with data validation support. All HTML elements are rendered using TT (Template-Toolkit) templates, which can be copied to your application folder and altered as you see fit providing the ultimate in flexibility when rendering HTML and Validating User Input. Form rending and input validation is done almost indentically to that of CPAN module Oogly::Aagly, please examine that POD (documentation) for a more indepth look at configuration.
What Dancer::Plugin::DataFu does is complex, but what it asks the user/developer to do is simple. This is what makes Dancer::Plugin::DataFu awesome amongst other things. Unlike HTML::FormFu, CGI::FormValidator, Form::Sensible, HTML::FormHandler and a whole host of other solutions, Dancer::Plugin::DataFu approaches HTML form rendering and data input validation in a very different manner. First and most importantly, data input (referred to as fields) are compartmentalized from the start. Meaning you define each field individually then group them within a profile, like columns exist in database tables, fields exist profiles but can be mixed together with other fields from other profiles to generate an array of forms without having to define each form specifically.
Second and of equal import, Dancer::Plugin::DataFu uses Template-Toolkit to define, style and render forms. All standard HTML form elements have pre-made templates that ship with Dancer::Plugin::DataFu and exist in your local Perl library. These template have been designed to automatically display inline error messages if form valdiation fails and also remembers posted form data, etc. If the default form style, error messages, sticky form functionality, etc are not enough for you, I have included a command-line utility to copy Dancer::Plugin::DataFu template to your current working directory for you manipulating pleasure, e.g.
$ dancer-datafu ... copied HTML template from ... to ... or $ dancer-datafu /tmp/elements ... copied HTML template from ... to /tmp/elements
Form elements can be rendered individually or as part of a form, custom form elements can be created and registered for automatic use in form generation, your template directory can be changed programmatically at runtime based on conditions, environment, etc.
All this is done while only requiring the developer to specify to most basic parameters.
plugins: DataFu: form: profiles: ./ grid: profiles: ./
Form and Grid profiles are like classes that encapsulate the individual fields required to render form or table elements. Profile are perl .pl files which returns a hashref that the rendering engines use to process your request. As stated above, the Dancer::Plugin::DataFu form and grid HTML TT templates can be copied to your current working directory for customization in which case you will need to tell the plugin where those templates are being stored.
plugins: DataFu: form: templates: views/datafu profiles: profiles grid: templates: views/datafu profiles: profiles/table
... myapp/profiles/user.pl # form profile example our $profile = { 'login' => { label => 'user login', required => 1, element => { type => 'input_text' } }, 'password' => { label => 'user password', required => 1, element => { type => 'input_text' } } };
Form profiles are where you would put all of your data input constraints and validation rules. Form rending and input validation is done almost indentically to that of CPAN module Oogly::Aagly, please examine that POD (documentation) for a more indepth look at configuration. All profiles in the specified 'profiles' directory are loaded automatically for DRY (don't repeat yourself) purposes, allowing you to mix fields from different profiles. The fields used in rendering and validation are stored and referenced using the profile name and field name together seperated with a single period (.), i.e. The login field example above would be referenced as ...
form->validate('user.login'); #profile: user, field: login
Requiring only the form name, action URL and a list of fields (profile.fieldname), Dancer::Plugin::DataFu makes form validation and generation fun and easy.
form->render('form', '/action', 'profile.field');
In some cases you may not want to use hardcoded .pl profile files, Dancer::Plugin::DataFu::Form provides a convenient accessor to allow you to manually define profiles on-demand.
my $form = form; $form->fields('field' => { label => '...', validation => sub { ... } }); $form->render('form', '/action');
Because field definitions can contain filters which alter internal copies of the GET and POST parameters passed in by the form, Dancer::Plugin::DataFu::Form provides a convenient accessor to those modified parameters.
my $form = form; $form->fields('full_name' => { required => 1, filters => [qw/strip trim camelcase/] }); $form->validate('full_name'); # get modified parameters my $params = $form->params;
A form profile is a Perl file (.pl file specifically) that returns a hashref which will be used to configure the form rendering engine that evals it. A form profile can have the following definitions.
# consider to following our $profile = { 'field_name' => { ... }, }; # each field may contain the following definitions label => 'human-readable field name' error => 'override the system standard error(s)' required => 1 min_length => 2 max_length => 10 ref_type => 'array', regex => '^\d+$' filters => [ 'trim', 'strip', 'lowercase', 'uppercase', 'camelcase', 'alphanumeric', 'numeric', 'alpha', 'digit', sub { # do something with @_ } ] validation => sub { my ($self, $field, $all_parameters) = @_; ... }
If you are familiar with TT (Template-Toolkit) then you already understand that you usually want to pass a few variables to the rendering engine when processing a template, if you didn't already know that, you do now. Each form template element is passed the following variables...
my $template_variable_object = { name => 'form name', url => 'form url', form => $form_class_object, field => $field_object_context, this => 'field name', vars => $additional_custom_variables_hashref };
... form.tt This template is a wrapper that encapsulates the selected form elements rendered.
... input_checkbox.tt 'profile.languages' => { element => { type => 'input_checkbox', options => [ { value => 100, label => 'English' }, { value => 101, label => 'Spanish' }, { value => 102, label => 'Russian' }, ] }, default => 100 }; ... or ... 'profile.languages' => { label => 'World Languages', element => { type => 'input_checkbox', options => { { value => 100, label => 'English' }, { value => 101, label => 'Spanish' }, { value => 102, label => 'Russian' }, } }, default => [100,101] };
... input_file.tt 'profile.avatar_upload' => { element => { type => 'input_file' } };
... input_hidden.tt 'profile.sid' => { element => { type => 'input_hidden' }, value => $COOKIE{SID} # or whatever };
... input_password.tt 'profile.password_confirm' => { element => { type => 'input_password' } };
... input_radio.tt 'profile.payment_method' => { element => { type => 'input_radio', options => [ { value => 100, label => 'Visa' }, { value => 101, label => 'MasterCard' }, { value => 102, label => 'Discover' }, ], default => 100 } };
... input_text.tt 'profile.login' => { element => { type => 'input_text' } };
... select.tt 'profile.payment_terms' => { element => { type => 'select', options => [ { value => 100, label => 'Net 10' }, { value => 101, label => 'Net 15' }, { value => 102, label => 'Net 30' }, ], } };
... select_multiple.tt 'profile.user_access_group' => { element => { type => 'select_multiple', options => [ { value => 100, label => 'User' }, { value => 101, label => 'Admin' }, { value => 102, label => 'Super Admin' }, ], } };
... textarea.tt 'profile.myprofile_greeting' => { element => { type => 'textarea', } };
... myapp/profiles/table/user.pl our $profile = { 'header' => 'example table header', 'columns' => [ { header => 'ID', bindto => 'id', element => { type => 'input_checkbox' } }, { header => 'Column One', bindto => 'col1', }, { header => 'Column Two', bindto => 'col2', }, { header => 'Action', data => sub { my ($self, $row) = @_; return $row->{col1}; }, } ], 'navigation' => sub { my ($self, $dataset) = @_; return 'Found ' . @{$dataset} . ' records'; } };
Grid profiles are where you would put all of your table configuration information. All grid profiles in the specified directory are loaded automatically allowing you easily select from different profiles. Like forms, rendering HTML tables is very straightforward requiring only a table name, dataset (arrayref of hashrefs), and the name of the profile. e.g.
my $dataset = [ { id => 100, col1 => 'column1a', col2 => 'column2a' }, { id => 101, col1 => 'column1b', col2 => 'column2b' }, { id => 102, col1 => 'column1c', col2 => 'column2c' }, { id => 103, col1 => 'column1d', col2 => 'column2d' }, { id => 104, col1 => 'column1e', col2 => 'column2e' }, { id => 105, col1 => 'column1f', col2 => 'column2f' }, { id => 106, col1 => 'column1g', col2 => 'column2g' }, { id => 107, col1 => 'column1h', col2 => 'column2h' }, { id => 108, col1 => 'column1i', col2 => 'column2i' }, { id => 109, col1 => 'column1j', col2 => 'column2j' }, ]; grid->render('name', 'profile_name', $dataset);
A grid profile is a Perl file (.pl file specifically) that returns a hashref which will be used to configure the grid rendering engine that evals it. A grid profile can have the following definitions.
# consider to following our $profile = { 'header' => '', 'columns' => [...], 'navigation' => sub { ...} }; # header obviously describes the table and renders a table header row # if omitted, does not render # columns is an arrayref where each element is a hashref that may # contain the following definitions header => 'column name' bindto => 'dataset column key' data => sub { my ($self, $row) = @_ # hint: return <a href="?action=edit&id=$row->{id}">Edit</a> ... } # navigation is a code reference that can be used to generate a # sophisticated navigation element at the beginning, end, or both ends # of the rendered table
my $template_variable_object = { name => 'grid name', prof => $grid_profile_object, colm => $column_object_context, vars => $additional_custom_variables_hashref };
... table.tt This template is a wrapper that encapsulates the headers and row elements rendered.
... tdata.tt This template is a wrapper that encapsulates any data not wrapped by another table template.
... thead.tt This template is uses to render the column headers.
... theader.tt This template is uses to render the table header.
... tnavigation.tt This template is uses to render the header or footer navigation.
... trow.tt This template is a wrapper that encapsulates the current table row.
The render method returns compiled html for the form or grid object that called it. Additionally you can pass a hashref of key/value pairs as the last argument to the render function to include additional variables in the processing of the template.
# form context $self->render( $form_name, $action_url, @profile_fields, \%more_vars ); # grid context $self->render( $grid_name, $profile_name, \@dataset );
The render_control method returns an HTML form element using the specified field name. This is useful when you need to break out of the canned form rendering layout and prefer to render the form fields individually.The render_control method may be passed one or many form field names.
$form->render_control('field_name'); # return multiple form elements as an array $form->render_control(@fields);
The templates method is used to define the relative path to where the HTML element templates are stored.
$form->templates('views/elements');
The template method is used to define the relative path to where the specified form element template is stored.
$form->template(input_text => 'elements/input_text.tt');
This method can also be used to specify the location of custom elements during runtime.
$form->template(js_tree => 'elements/js_tree.tt');
I suppose I should mention that the form validation and rendering portion of this package has been ported from Oogly::Aagly which means that it has undergone a lot more testing and design than the grid rendering portion. So I hope you will be a little patient, I'll make sure I catch up.
Al Newkirk <awncorp@cpan.org>
This software is copyright (c) 2010 by awncorp.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
To install Dancer::Plugin::DataFu, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Dancer::Plugin::DataFu
CPAN shell
perl -MCPAN -e shell install Dancer::Plugin::DataFu
For more information on module installation, please visit the detailed CPAN module installation guide.