The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Dancer::Plugin::DataFu - Dancer HTML Form and Grid/Table engine with Input Validation

VERSION

version 1.103070

SYNOPSIS

    # 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]);
    };

DESCRIPTION

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.

WHY DataFu IS GREAT

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.

CONFIGURATION

    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

FORM PROFILES

    ... 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;

FORM PROFILE SPECIFICATION

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) = @_;
        ...
    }

FORM TEMPLATES

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
    ... form.tt
    This template is a wrapper that encapsulates the selected form elements
    rendered.
input_checkbox.tt
    ... 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
    ... input_file.tt
    
    'profile.avatar_upload' => {
        element => {
            type => 'input_file'
        }
    };
input_hidden.tt
    ... input_hidden.tt
    
    'profile.sid' => {
        element => {
            type => 'input_hidden'
        },
        value => $COOKIE{SID} # or whatever
    };
input_password.tt
    ... input_password.tt
    
    'profile.password_confirm' => {
        element => {
            type => 'input_password'
        }
    };
input_radio.tt
    ... 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
    ... input_text.tt
    
    'profile.login' => {
        element => {
            type => 'input_text'
        }
    };
select.tt
    ... 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
    ... 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
    ... textarea.tt
    
    'profile.myprofile_greeting' => {
        element => {
            type => 'textarea',
        }
    };

GRID PROFILES

    ... 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);

GRID PROFILE SPECIFICATION

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

GRID TEMPLATES

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  => 'grid name',
        prof  => $grid_profile_object,
        colm  => $column_object_context,
        vars  => $additional_custom_variables_hashref
    };
table.tt
    ... table.tt
    This template is a wrapper that encapsulates the headers and row elements
    rendered.
tdata.tt
    ... tdata.tt
    This template is a wrapper that encapsulates any data not wrapped by another
    table template.
thead.tt
    ... thead.tt
    This template is uses to render the column headers.
theader.tt
    ... theader.tt
    This template is uses to render the table header.
tnavigation.tt
    ... tnavigation.tt
    This template is uses to render the header or footer navigation.
trow.tt
    ... trow.tt
    This template is a wrapper that encapsulates the current table row.

METHODS

render

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 );

render_control

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);

templates

The templates method is used to define the relative path to where the HTML element templates are stored.

    $form->templates('views/elements');

template

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');

DISCLAIMER

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.

AUTHOR

Al Newkirk <awncorp@cpan.org>

COPYRIGHT AND LICENSE

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.