HTML::FormHandler::ManUser:Contributed PerlHTML::FormHandler::Manual::Intro(3)NAMEHTML::FormHandler::Manual::Intro - introduction to using FormHandler
VERSION
version 0.35005
SUMMARY
HTML::FormHandler is a form handling class that validates HTML form
data and, for database forms, saves it to the database on validation.
It has field classes that can be used for creating a set of widgets and
highly automatic templates. There are two simple rendering roles,
HTML::FormHandler::Render::Simple and a subclass of it
HTML::FormHandler::Render::Table, plus a set of widget roles for
individual form and field classes. FormHandler is designed to make it
easy to produce alternative rendering modules.
The DBIC & CDBI models will save form fields automatically to the
database, will retrieve selection lists from the database (with type =>
'Select' and a fieldname containing a single relationship, or type =>
'Multiple' and a many_to_many relationship), and will save the selected
values (one value for 'Select', multiple values in a mapping table for
a 'Multiple' field).
The 'form' is a Perl subclass of HTML::FormHandler for non-database
forms, or a subclass of a model class for database forms, and in it you
define your fields and validation routines. Because it's a Perl class,
you have a lot of flexibility.
You can use transformations, Moose type constraints, and coercions,
listed in the field's 'apply' attribute, to validate or inflate the
fields (see "apply" in HTML::FormHandler::Field). You can define your
own HTML::FormHandler::Field classes to create your own field types,
and perform specialized validation. You can create MooseX::Types
libraries and use them to perform field validation.
The HTML::FormHandler::Model::DBIC package includes a working example
using a SQLite database and a number of forms in the test directory.
You can execute the sample from a downloaded DBIC model distribution
package with:
perl -Ilib t/script/bookdb_server.pl
Basics
You can either create a form dynamically with "my $form =
HTML::FormHandler->new( <arguments>);" (or with
"HTML::FormHandler::Model::DBIC->new(...)" for DBIC forms ) or by
creating a form subclass. There are some features and usage patterns
which do not fit well with the dynamic method - it's harder to create
form methods or attributes, for example. But most forms could be
created in either way.
An example of a form class, subclassed from
HTML::FormHandler::Model::DBIC. The fields are declared here using the
'has_field' sugar, but they could also be defined using the
'field_list', as in the following dynamic example.
package MyApp:Form::User;
use HTML::FormHandler::Moose;
extends 'HTML::FormHandler::Model::DBIC';
# Associate this form with a DBIx::Class result class
# Or 'item_class' can be passed in on 'new', or you
# you can always pass in a row object
has '+item_class' => ( default => 'User' );
# Define the fields that this form will operate on
# Field names are usually column, accessor, or relationship names in your
# DBIx::Class result class. You can also have fields that don't exist
# in your result class.
has_field 'name' => ( type => 'Text', label => 'Username', required => 1,
unique => 1, messages => { required => 'You must enter a username',
unique => 'That username is already taken' } );
# the css_class, title, and widget attributes are for use in templates
has_field 'age' => ( type => 'PosInteger', required => 1, css_class => 'box',
title => 'User age in years', widget => 'age_text', range_start => 18 );
has_field 'sex' => ( type => 'Select', label => 'Gender', required => 1 );
# a customized field class
has_field 'birthdate' => ( type => '+MyApp::Field::Date' );
has_field 'hobbies' => ( type => 'Multiple', size => 5 );
has_field 'address' => ( type => 'Text' );
has_field 'city' => ( type => 'Text' );
has_field 'state' => ( type => 'Select' );
has '+dependency' => ( default => sub {
[
['address', 'city', 'state'],
],
}
);
no HTML::FormHandler::Moose;
1;
An example of a dynamically created form instance:
use HTML::FormHandler;
my @select_options = ( {value => 1, label => 'One'},
{value => 2, label => 'Two'}, {value => 3, label => 'Three'} );
my $args = {
name => 'test_form',
field_list => [
'username' => {
type => 'Text',
apply => [ { check => qr/^[0-9a-z]*/,
message => 'Contains invalid characters' } ],
},
'password' => {
type => 'Password',
},
'some_number' => {
type => 'IntRange',
range_min => 12,
range_max => 291,
},
'some_option' => {
type => 'Checkbox',
checkbox_value => 'yes',
input_without_param => 'no'
},
'some_text' => {
type => 'TextArea',
},
'hidden_field' => {
type => 'Hidden',
default => 'foo',
},
'upload_file' => {
type => 'Upload',
max_size => 262144,
},
'select_foo' => {
type => 'Select',
options => \@select_options,
},
'select_bar' => {
type => 'Select',
options => \@select_options,
multiple => 1,
size => 4,
},
'select_baz' => {
type => 'Select',
options => \@select_options,
widget => 'radio_group',
},
'select_bax' => {
type => 'Select',
options => \@select_options,
multiple => 1,
widget => 'checkbox_group'
},
'sub' => {
type => 'Compound',
},
'sub.user' => {
type => 'Text',
apply => [ { check => qr/^[0-9a-z]*/,
message => 'Not a valid user' } ],
},
'sub.name' => {
type => 'Text',
apply => [ { check => qr/^[0-9a-z]*/,
message => 'Not a valid name' } ],
},
'reset' => {
type => 'Reset',
},
'submit' => {
type => 'Submit',
},
'some_link' => {
type => 'Display',
html => '<a href="http://google.com/">Go somewhere</>',
},
]
};
my $form = HTML::FormHandler->new( %$args );
my $renderedform = $form->render;
You can create custom rendering roles or classes and easily change the
rendering methods. See HTML::FormHandler::Manual::Rendering for
details.
By default the HTML::FormHandler::Widget roles will be applied, making
it possible to render a simple form for scaffolding:
[% form.render %]
Individual fields can also be rendered:
[% form.field('title').render %]
Note: If you don't want to use the field widgets, you can set the
'no_widgets' flag in the form to skip having the widget roles applied
to the fields.
You can render a field using a template. For an input field:
<p>
[% f = form.field('address') %]
<label class="label" for="[% f.name %]">[% f.label %]:</label>
<input type="text" name="[% f.name %]" id="[% f.name %]" value="[% f.fif %]">
</p>
The value can come from the hash returned by "$form->fif", from the
'fif' attribute of the field, or can be supplied by FillInForm and the
Catalyst::View::FillInForm role on your view class. Plain HTML works
fine for a simple input field if you use FillInForm to supply the
value.
For a select list, provide a relationship name as the field name, or
provide an options_<field_name> subroutine in the form. You need to
access the field 'options' to create a select list. You could, of
course, create the select lists by hand or some other way, but if you
don't use the same method that is used by your FormHandler Select
field, you risk getting out of sync. TT example:
<p>
[% f = form.field('sex') %]
<label class="label" for="[% f.name %]">[% f.label %]</label>
<select name="[% f.name %]">
[% FOR option IN f.options %]
<option value="[% option.value %]"
[% IF option.value == f.fif %]selected="selected"[% END %]>
[% option.label | html %]</option>
[% END %]
</select>
</p>
A multiple select list where 'hobbies' is a 'many_to_many' pseudo-
relationship. (field attributes: sort_column, label_column,
active_column).
<p>
[% f = form.field('hobbies') %]
<label class="label" for="[% f.name %]">[% f.label || f.name %]</label>
<select name="[% f.name %]" multiple="multiple" size="[% f.size %]">
[% FOR option IN f.options %]
<option value="[% option.value %]" [% FOREACH selval IN f.fif %][% IF selval == option.value %]selected="selected"[% END %][% END %]>[% option.label | html %]</option>
[% END %]
</select>
</p>
In a Catalyst controller:
package MyApp::Controller::User;
use Moose;
BEGIN { extends 'Catalyst::Controller' }
use MyApp::Form::User
has 'form' => ( isa => 'MyApp::Form::User', is => 'rw',
lazy => 1, default => sub { MyApp::Form::User->new } );
# Create or edit
sub edit : Local {
my ( $self, $c, $user_id ) = @_;
$c->stash(
form => $self->form,
template => 'user/edit.tt' );
return unless $self->form->process( item_id => $user_id,
schema => $c->model('DB')->schema );
# Form validated.
$c->stash( user => $self->form->item );
$c->res->redirect($c->uri_for('profile'));
}
1;
With the DBIC model the schema is set from the 'item' (row object)
passed in, or from the primary key ('item_id') and schema. You might
also want to pass in the 'action' to which the form will be submitted
if you're using FormHandler's renderer and if it's important to pass
XHTML validation:
$form->process( action => <valid url>, item => $row,
params => $c->req->params );
The example above uses persistent forms in a Moose attribute. The
'process' method will clear out non-persistent form values and update
the information from the database row (if given). You can also create
a new form on each request with new:
my $form = BookDB::Form::Book->new( item => $book );
return unless $form->process( params => $c->req->parameters );
There is also a 'validated' flag:
$form->process( params => $c->req->parameters );
return unless $form->validated;
Form processing is a two-pass operation. The first time through the
parameters will be an empty hashref, since the form has not been
submitted yet. FormHandler will load values from the database object
(item_id/schema or item) or from an 'init_object', and return false
because the form has not validated yet. At this point the 'return' (in
Catalyst) will cause the renderview processing to take place and the
form will be displayed with initialized values (from a template using
the 'fif' values or from HTML::FillInForm) to allow user input.
When the form is submitted, the action in the HTML form's 'action'
value will be called (the same one that just displayed the form
usually), and the second pass of calling the FormHandler process method
will occur.
This time there WILL be values in the parameters, and FormHandler will
call the validation routines. If the validation succeeds, FormHandler
will return a 'true' value, and execution will fall through to after
the "return unless ...." line. At this point you will either redirect
to some other page, or in some cases redisplay the form with a message
that saving succeeded. If the validation fails, the 'return' will cause
the form to be displayed again.
The values to be used to fill in your form are automatically created by
FormHandler, and are available in the field's 'fif' attribute:
$field->fif
or in the form's fif hash, which will contain the fill-in-form values
for all the form's fields:
$form->fif
If you want to use HTML::FillInForm to fill in values instead of the
doing it in directly in a template using either the field or the form
'fif' methods, you can use Catalyst::View::FillInForm on your view
class and set the 'fif' hash in the 'fillinform' stash variable:
$self->form->process( ... );
$c->stash( fillinform => $self->form->fif );
return unless $form->validated;
Note that FormHandler by default uses empty params as a signal that the
form has not actually been posted, and so will not attempt to validate
a form with empty params. Most of the time this works OK, but if you
have a small form with only the controls that do not return a post
parameter if unselected (checkboxes and select lists), then the form
will not be validated if everything is unselected. For this case you
can either add a hidden field, or use the 'posted' flag:
$form->process( posted => ($c->req->method eq 'POST', params => ... );
The corollary is that you will confuse FormHandler if you add extra
params. It's often a better idea to add Moose attributes to the form
rather than 'dummy' fields if the data is not coming from a form
control.
Form generator
A DBIC form generator is installed with the
HTML::FormHandler::Model::DBIC package. See
HTML::FormHandler::Generator::DBIC.
There's also a role, HTML::FormHandler::TraitFor::DBICFields, that
allows simple form fields to be auto-generated from a DBIC result
class.
my $form = HTML::FormHandler::Model::DBIC->new_with_traits(
traits => ['HTML::FormHandler::TraitFor::DBICFields'],
include => ['title', 'author' ],
field_list => [ 'submit' => { type => 'Submit', value => 'Save', order => 99 } ],
item => $book );
Non-database forms
The base class for a non-database form is HTML::FormHandler instead of
a model class. You do not initialize a non-database form with an item
or item_id, although you can use an init_object for the initial values.
After validation, you can get a hashref of values back from the 'value'
method.
return unless $form->validated;
my $result = $form->value;
The 'value' structure is what FormHandler uses to update the database.
Form Models
For a database form, use a model base class that interfaces with the
database, such as HTML::FormHandler::Model::DBIC, which needs to be
installed as a separate package.
When using a database model, form field values for the row are
retrieved from the database using the field 'accessor' attributes
(defaults to field name) as database class accessors.
FormHandler will use relationships to populate single and multiple
selection lists, and validate input. A 'single' relationship is
processed by HTML::FormHandler::Field::Compound. A 'has_many'
relationship is processed by HTML::FormHandler::Field::Repeatable.
You can pass in either the primary key and or a row object to the form.
If a primary key (item_id) is passed in, you must also provide the
schema. The model will use the item_class (DBIC source name) to fetch
the row from the database. If you pass in a row object (item), the
schema, source_class, and item_id will be set from the row.
The "$form->process" will validate the parameters and then update or
create the database row object.
Field names
The standard way to use FormHandler is with field names that match your
database accessors. If you want to prepend the HTML field names with a
name plus dot, you can set the form 'name' and use the 'html_prefix'
flag. "$name." will be stripped from the beginning of the HTML fields
before processing by HFH, and will be added back in 'fif'. The field's
'html_name' convenience attribute will return this name for use in
templates.
If you want the FormHandler field name to be different than the
database accessor, set 'accessor' on your fields. (It defaults to the
field name). You could then use any name that you want for your field.
There are a number of name related field attributes. The 'name' is the
name used to identify this particular field in this fields array. The
'full_name' is includes the names of all parents of this field, like
'address.street.streetname'. The 'html_name' is the same as the
'full_name' unless you have set the 'html_prefix' flag, in which case
it includes the form name: 'myform.address.street.streetname'.
To retrieve a field by name, you can use either the full_name or a
chain: "$form->field('address')->field('street')->field('streetname')"
or: "$form->field('address.street.streetname')".
You can process multiple FormHandler forms at the same time (using the
same HTML form) with multiple form objects and multiple process/update
calls. You would have to ensure that there are no duplicate field
names, possibly by using 'html_prefix'.
has_field
This is not actually a Moose attribute. It is sugar to allow the
declarative specification of fields. It will not create accessors for
the fields. The 'type' is not a Moose type, but an
HTML::FormHandler::Field class name. To use this sugar, you must do
use HTML::FormHandler::Moose;
instead of " use Moose; ". (Moose best practice advises using " use
namespace::autoclean; " or putting " no HTML::FormHandler::Moose; " at
the end of the package to keep the namespace clean of imported
methods.)
To declare fields use the syntax:
has_field 'title' => ( type => 'Text', required => 1 );
has_field 'authors' => ( type => 'Select' );
instead of:
has 'field_list' => ( default => sub {
[
title => {
type => 'Text',
required => 1,
},
authors => 'Select',
]
}
);
Fields specified in a field_list are processed after fields specified
with 'has_field'. After processing, fields live in the 'fields' array,
and can be accessed with the field method: "$form->field('title')".
Forms with 'has_field' field declarations may be subclassed. Or use
HTML::FormHandler::Moose::Role to create roles with the 'has_field'
syntax:
package Form::Role::Address;
use HTML::FormHandler::Moose::Role;
has_field 'street' => ( type => 'Text', size => '50' );
has_field 'city' => ( type = 'Text', size => 24 );
has_field 'state' => ( type => 'Select );
has_field 'zip' => ( type => '+Zip', required => 1 );
no HTML::FormHandler::Moose::Role;
1;
You can use roles to define fields and validations and include them in
form classes using 'with':
package Form::Member;
use HTML::FormHandler::Moose;
with 'Form::Role::Person';
with 'Form::Role::Address';
extends 'HTML::FormHandler::Model::DBIC';
has_field 'user_name' => ( type => 'Text', required => 1 );
no HTML::FormHandler::Moose;
1;
If you prefix the field name with a '+' the attributes in this
definition will modify existing attributes or be added to an existing
field definition:
has_field 'user' => ( type => 'Text', ...., required => 1 );
....
has_field '+user' => ( required => 0 );
The form field_list
An array of name, specification pairs to define fields.
The field_list is one way to define the fields in your form.
has '+field_list' => ( default => sub {
[
field_one => {
type => 'Text',
required => 1
},
field_two => 'Text,
]
}
);
An example of a select field:
sub field_list {
return [
favorite_color => {
type => 'Select',
label_column => 'color_name',
active_column => 'is_active',
},
];
}
Fields
A form's fields are created from the 'has_field' and 'field_list'
definitions. FormHandler processes the field lists and creates an
array of HTML::FormHandler::Field objects. The "type" of a field
determines which field class to use. The field class determines which
attributes are valid for a particular field. A number of field classes
are provided by FormHandler. You can customize the validation in your
form on a per field basis, but validation that will be used for more
than one field might be more easily handled in a custom field class.
In the templates the fields are accessed with " form.field('name') ".
Field errors are in "$field->errors".
The fields are assumed to be in the HTML::FormHandler::Field name
space. If you want to explicitly list the field's package, prefix it
with a plus sign. The field_name_space plus the default name spaces
'HTML::FormHandler::Field' and 'HTML::FormHandlerX::Field' will be
searched for fields.
has '+field_name_space' => ( default => 'MyApp::Form::Field' );
has_field 'name' => ( type => 'Text' ); # HTML::FormHandler::Field::Text
has_field 'name' => ( type => '+My::FieldType' ); # My::Fieldtype
has_field 'foo' => ( type => +Foo' ); # MyApp::Form::Field::Foo
or
has_field 'foo' => ( type => 'Foo' ); # MyApp::Form::Field::Foo
The most basic type is "Text", which takes a single scalar value. (If
the type of a field is not specified, it will be set to 'Text'.) A
"Select" class is similar, but its value must be a valid choice from a
list of options. A "Multiple" type is like "Select" but it allows
selecting more than one value at a time.
Each field has a "value" method, which is the field's internal value.
This is the value your database object would have (e.g. scalar, boolean
0 or 1, DateTime object).
When data is passed in to validate the form, the input is copied into
the 'value' attribute of the field, and the actions specified by
'apply' will be performed on the value. This includes the 'trim'
transform which will strip leading and trailing spaces. After the
actions are applied, the field's 'validate' method is called.
Compound fields
A compound field is a field that has sub-fields. Compound fields can be
created in two ways: 1) using a field class, 2) by declaration.
To create a compound field class, you must extend
HTML::FormHandler::Field::Compound and use HTML::FormHandler::Moose to
allow declaring fields:
package MyApp::Field::Duration;
use HTML::FormHandler::Moose;
extends 'HTML::FormHandler::Field::Compound';
has_field 'month' => (type => 'Integer');
has_field 'day' => ( type => 'Integer' );
has_field 'minutes' => ( type => 'Integer' );
Then in the form:
has_field 'my_duration' => ( type => '+Duration' );
To create a compound field by declaration, declare the containing
compound field and subfields, prefixing the subfield names with the
name of the containing compound field plus a dot:
package MyApp::Form;
use HTML::FormHandler::Moose;
extends 'HTML::FormHandler';
has_field 'duration' => ( type => 'Compound' );
has_field 'duration.month' => ( type => 'Integer' );
has_field 'duration.day' => ( type => 'Integer' );
has_field 'duration.year' => ( type => 'Integer' );
In an HTML form the name of the field must be the complete name with
dots. The 'html_name' field attribute can be used to get this name:
[% field.html_name %]
A compound field can be used for a database relation that will have
only one row (belongs_to or has_one).
Repeatable fields
Repeatable fields are used for arrays of compound fields.
has_field 'addresses' => ( type => 'Repeatable' );
has_field 'addresses.address_id' => ( type => 'PrimaryKey' );
has_field 'addresses.street';
has_field 'addresses.city';
has_field 'addresses.country' => ( type => 'Select' );
The arrays will be built from arrays passed in the params, or from
related ('has_many') rows in the database.
It is also used for arrays of single fields using the 'contains'
keyword:
has_field 'tags' => ( type => 'Repeatable' );
has_field 'tags.contains' => ( type => '+Tag' );
See HTML::FormHandler::Field::Repeatable for more information.
Filters, transformations, and constraints
HTML::FormHandler has a flexible system of of filters and constraints.
You can use Moose types to constrain the allowable values in a field
and use coercions to inflate the HTML field input, such as for a
DateTime. You can also create non-Moose transformations and
constraints. See the 'apply' attribute in HTML::FormHandler::Field.
has_field 'some_field' => ( apply => [ 'MooseType',
{ transform => sub {...}, message => 'xxxx' },
{ check => sub { ... }, message => 'xxxx' } ] );
The actions in the 'apply' array will be performed in the order they
are specified, allowing fine-grained control over inflation and
validation.
You can also create a simple subroutine in your form class to perform
validation. The default name of this subroutine is
'validate_<fieldname>', but the name can also be set in the field with
the 'set_validate' attribute. (This method is not called when the field
is empty. Use 'required'/'required_message' for that case.)
If you need to access form attributes such as the schema, the
'set_validate' subroutine may be preferable, but most validations can
be performed using either method.
Inflation/deflation
The most common object that needs inflation and deflation is a DateTime
field, such as would come from a DBIC result class if you are using the
InflateColumn plugin, but it could be any object, such as a Duration.
Inflation can be done in one of the 'actions' that are applied to the
field class or in any place that validation is done, though it is
probably better to do it in a field class. Usually the form's
validate_<field_name> methods should expect to get the already inflated
value (unless inflation failed).
Deflation is done to convert the object that is retrieved from the
model to a format that can be displayed in an HTML form. Deflation is
always performed when retrieving from the initial object. If an input
value exists for a field that value is usually used to re-display the
field, and deflation is NOT performed, unless the 'fif_from_value' flag
is set for the field. (See HTML::FormHandler::Field). This might be
desirable if you want to canonicalize the entered data into a standard
form.
Creating custom fields
Subclass a custom field from HTML::FormHandler::Field, or one of the
existing subclasses. Almost everything that is done in a custom field
class can also be done in a form. The advantage of a field class is
that it can simplify declaration of often-repeated sets of attributes.
The simplest subclasses contain only a 'validate' routine or an 'apply'
attribute, which is called by the base Field class from 'process'. Look
at HTML::FormHandler::Field::Email, for example.
If the field's value will be an object instead of a simple scalar, such
as a DateTime and you want to use the transformed value to fill in the
form, then you will also need a deflation or field class 'deflate'
method to reformat the object into a form suitable for an HTML form
field.
Some custom fields might only require setting certain attributes to
defaults, such as the HTML::FormHandler::Field::Hour field, which set
'range_start' to 0 and 'range_end' to 23. A 'select' field might
override the 'build_options' builder for the 'options' array, like
HTML::FormHandler::Field::IntRange. A field may add additional
attributes, such as 'label_format' in
HTML::FormHandler::Field::IntRange, or set the 'required' message.
An alternative to new field classes for many field validations might be
roles with collections of validations.
Common form attributes
The 'dependency' field_list key is an array of arrays of field names.
During validation, if any field in a given group contains the pattern
/\S/ (non-blank), the 'required' flag is set for all of the fields in
the group.
has '+dependency' => ( default => sub {
[
['address', 'city', 'state', 'zip'],
['cc_no', 'cc_expires'],
],
},
);
The 'item_class':
has '+item_class' => ( default => 'Book' );
The form name:
has '+name' => ( default => 'book_form' );
The field name space for use with '+' prefixed fields:
has '+field_name_space' => ( default => 'MyApp::Form::Field' );
...
has_field 'subname' => ( type => '+SubName' );
An 'init_object' for filling in the form with default values instead of
the database object. (To set individual field values use
"default_$fieldname" or default => '...' on the field. Individual field
defaults take precedence over an init_object or model item.)
has '+init_object' => ( default => sub {
{ name => 'Choose name',
project => 'Standard'
}
}
);
sub init_object { ... }
Other methods for your form
options_$fieldname
If you have a 'Select' or 'Multiple' field, there are three ways to
provide the 'options', or the list of values and labels for the
select list. 1) Get them automatically from a database table (from
the relationship that is the field name/accessor), 2) provide them
in the field's 'options' attribute, or 2) provide them from an
options_$fieldname method in the form.
An 'options_$fieldname' method should return a list of ordered key
(option value) and value (label to be displayed in the select list)
pairs.
sub options_fruit {
return (
1 => 'apples',
2 => 'oranges',
3 => 'kiwi',
);
}
You can also write custom methods to retrieve the option info from
the database:
sub options_country {
my $self = shift;
return unless $self->schema;
my @rows =
$self->schema->resultset( 'Country' )->
search( {}, { order_by => ['rank', 'country_name'] } )->all;
return [ map { $_->digraph, $_->country_name } @rows ];
}
default_$fieldname
Allows you to provide (in the form) a different initial value for a
particular field than that in the database (formerly
init_value_$fieldname ).
sub default_license {
my ( $self, $field, $item ) = @_;
return 0 unless $item && $item->license_id;
return $item->license_id;
}
Defaults can also be provided by the field's 'default' attribute
and the init_object.
validate_$fieldname
Instead of using field constraints, you can choose to do per-field
validation customization in a form method:
sub validate_age {
my ( $self, $field ) = @_;
$field->add_error('Sorry, you must be 18')
if $field->value < 18;
}
A different form method name for this can be specified with the
field's 'set_validate' attribute:
has_field 'age' => ( type => 'Text', set_validate => 'check_age' );
sub check_age {
...
}
This method is not called when the field is empty.
validate
Handle cross-field validation, or any validation that needs to be
done after the entire form is validated. This form method is
executed whether or not the form has validated so far.
sub validate {
my $self = shift;
if ( $self->field('count')->value && $self->field('duration')->value )
{
$self->field('duration')->add_error(
'Do not enter both a count and a duration' );
}
}
update_model
Override the model's 'update_model' method to do additional
updates.
sub update_model {
my $self = shift;
$self->next::method;
my $event = $self->item;
$event->update( ... );
}
You may want to use DBIC's 'txn_do' to keep updates in the same
transaction. See HTML::FormHandler::Manual::Cookbook for an
example.
Filling the HTML form with values
There are three ways to get the database or parameter values into the
actual HTML form.
You can use the field method 'fif' (where "f" is "form.field('book')"
):
[% f.fif %]
You can use the hash returned by the form method "fif":
[% form.fif.book %]
Or you can use HTML::FillInForm (and Catalyst::View::FillInform if you
are using Catalyst) and the "$form->fif hash".
If you are already using FormHandler field attributes in your form
elements, then using the field 'fif' method is probably easiest. If you
are not using FormHandler field attributes, then your choice is between
using form.fif and FillInForm.
If you are not using FormHandler select lists and you use FillInForm,
then it is possible to have FormHandler process HTML forms that have no
template references to the form object at all, as long as the field
names are correct. If you think that FillInForm is evil, then you
could manage with only using FormHandler to fill in the form.
Using FormHandler with hand-built forms
You can use FormHandler to validate your data (and load it into a
database if you choose) with nothing from FormHandler in the templates
or HTML at all. The 'name' for the HTML form fields must match the HFH
field names. If you have compound or repeatable fields, the field names
must follow the HFH naming convention. (See the documentation for
Compound and Repeatable fields.)
In order to load the form with values, you should use HTML::FillInForm,
and Catalyst::View::FillInForm if you're using Catalyst. You must put
the FormHandler 'fif' hash into the Catalyst stash.
$form->process($params);
$c->stash( fillinform => $form->fif );
return unless $form->validated;
(Or you could use one of the options described in the previous section
for minimal interaction with the FormHandler form.)
If you have select lists and use the FormHandler 'Select' field types,
you need to be careful that the select lists don't get out of sync.
Testing
It's much easier to write unit tests for FormHandler forms than for
Catalyst controllers. The 't' directory of the downloaded distribution
has lots of examples. Here is an example of a test script for a DBIC
form:
use Test::More tests => 14;
use lib 't/lib';
use_ok( 'BookDB::Form::Book');
use_ok( 'BookDB::Schema::DB');
my $schema = BookDB::Schema::DB->connect('dbi:SQLite:t/db/book.db');
ok($schema, 'get db schema');
my $form = BookDB::Form::Book->new(schema => $schema);
# This is munging up the equivalent of param data from a form
my $good = {
'title' => 'How to Test Perl Form Processors',
'author' => 'I.M. Author',
'genres' => [2, 4],
'format' => 2,
'isbn' => '123-02345-0502-2' ,
'publisher' => 'EreWhon Publishing',
};
ok( $form->process( params => $good ), 'Good data' );
my $book = $form->item;
END { $book->delete };
ok ($book, 'get book object from form');
my $num_genres = $book->genres->count;
is( $num_genres, 2, 'multiple select list updated ok');
is( $form->field('format')->value, 2, 'get value for format' );
my $bad_1 = {
notitle => 'not req',
silly_field => 4,
};
ok( !$form->process( $bad_1 ), 'bad 1' );
my $bad_2 = {
'title' => "Another Silly Test Book",
'author' => "C. Foolish",
'year' => '1590',
'pages' => 'too few',
'format' => '22',
};
ok( !$form->process( $bad_2 ), 'bad 2');
ok( $form->field('year')->has_errors, 'year has error' );
ok( $form->field('pages')->has_errors, 'pages has error' );
ok( !$form->field('author')->has_errors, 'author has no error' );
ok( $form->field('format')->has_errors, 'format has error' );
my $good = {
title => "Another Silly Test Book",
author => "C. Foolish",
year => 1999,
pages => 101,
format => 2
};
ok( $form->process($good), 'now form validates' );
Messages
The base field class and the field subclasses have some 'built-in'
error messages. These can be modified by setting the 'messages'
hashref in the form or the individual fields.
When a message is retrieved in a field with
"$field->get_message('upload_file_')" for example, the 'get_message'
method will look first in user-set field specific messages, then in
user-supplied form messages, finally in messages provided by the field
classes.
package MyApp::Form;
use HTML::FormHandler::Moose;
extends 'HTML::FormHandler';
sub build_messages {
return { required => '....', my_message => '....' };
}
...
my $form = MyApp::Form->new( messages => { required => '...', ...} );
...
has_field 'my_field' => ( messages => { required => 'Please provide a my_field' },
required => 1 );
Localization
FormHandler's built-in errors are added to the form fields with
"$field->add_error", and to the form with "$form->add_form_error".
These methods call a "$self->_localize method" which is a coderef set
from the field's default_localize sub, the field's 'localize_meth'
attribute with "localize_meth => sub {}", or a form's sub
localize_meth. The default localize uses Locale::Maketext. You can
also use duck_type classes for localization. See the documentation in
" HTML::FormHandler::TraitFor::I18N " and the tests in xt/locale.t.
If you wish to skip localization for a particular message (such as for
system errors) you can use "$field->push_errors" or
"$form->push_form_errors".
Performance
FormHandler makes heavy use of Moose, so almost all of FormHandler's
profiled time will actually be in Moose methods. Some people prefer to
use a persistent form class (in a Moose attribute) in order to skip the
form building step on each call. Other people don't like that solution
because state will remain in the form until the next process call. The
'clear' method is called at the beginning of each 'process', but
additional Moose attributes in the form, etc, will have to cleared by
the programmer (with a method modifier on 'clear').
If you are loading options from the database and you don't need to have
them refreshed each time, you can set the 'do_not_reload' flag in the
Select/Multiple field. If you're not using the field widget roles, you
can set the 'no_widgets' flag. If you always use 'process' on each
call (recommended) then you can set the 'no_preload' flag in the form
to skip building results in BUILD (new).
AUTHOR
FormHandler Contributors - see HTML::FormHandler
COPYRIGHT AND LICENSE
This software is copyright (c) 2011 by Gerda Shank.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
perl v5.14.12011-10-0HTML::FormHandler::Manual::Intro(3)