NAME
HTML::FormHandler::Manual::Cookbook - FormHandler use recipes
SYNOPSIS
Collection of use recipes for HTML::FormHandler
Passing in even fewer parameters
If you like to bring the number of parameters down to the absolute minimum, you could either create a base controller that would set the schema and params in the form on each call using Catalyst::Component::InstancePerContext, or set them in a base Chained method. For a persistent form:
has 'edit_form' => ( isa => 'BookDB::Form::Book', is => 'rw',
lazy => 1, default => sub { BookDB::Form::Book->new } );
sub book_base : Chained PathPart('book') CaptureArgs(0)
{
my ( $self, $c ) = @_;
$self->edit_form->clear;
$self->edit_form->schema( $c->model('DB')->schema );
$self->edit_form->params( $c->req->parameters );
}
or if you want a new form on each call:
has 'edit_form' => ( isa => 'BookDB::Form::Book', is => 'rw' );
sub book_base : Chained PathPart('book') CaptureArgs(0)
{
my ( $self, $c ) = @_;
$self->edit_form( MyApp::Form->new( schema => $c->model('DB)->schema,
params => $c->req->parameters ) );
}
Then you could just pass in the item_id. You could simplify even that by creating a method that takes a simple ID. (You have to use 'update' here instead of 'process', because 'process' would clear out the schema and params you've already set.)
sub form {
my ( $self, $id ) = @_;
return $self->form->update( item_id => $id );
}
and in your controller action:
return unless $self->form($id);
Select lists
If you want to set the initial value of a select field to 0 (or some other default):
sub init_value_license {
my ( $self, $field, $item ) = @_;
return 0 unless $item && $item->license_id;
return $item->license_id;
}
If the table defining the choices for a select list doesn't include a 'no choice' choice, in your template:
[% f = form.field('subject_class') %]
<select id="select_sc" name="[% f.name %]">
<option value="">--- Choose Subject Class---</option>
[% FOR option IN f.options %]
<option value="[% option.value %]"
[% IF option.value == f.fif %]selected="selected"[% END %]>
[% option.label | html %]</option>
[% END %]
</select>
Or customize the select list in an 'options_' method:
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 ];
}
The database and FormHandler forms
If you have to process the input data before saving to the database, and this is something that would be useful in other places besides your form, you should do that processing in the DBIx::Class result class.
If the pre-processing is only relevant to HTML form input, you might want to do it in the form.
fields => {
'my_complex_field' => {
type => 'Text',
noupdate => 1,
}
}
The 'noupdate' flag is set in order to skip an attempt to update the database for this field (it would not be necessary if the field doesn't actually exist in the database...). You can process the input for the non-updatable field field in a number of different places, depending on what is most logical. Some of the choices are:
1) cross_validate
2) validate_model
3) model_update
The field 'clear' flag will cause that column in the database to be set to null. This flag could be set in a validation routine.
When the field is flagged 'writeonly', the value from the database will not be used to fill in the form (put in the $form->fif
hash, or the field $field->fif
), but a value entered in the form WILL be used to update the database.
If you want to enter fields from an additional table that is related to this one in a 'single' relationship, you can use the DBIx::Class 'proxy' feature to create accessors for those fields.
Set up form base classes or roles for your application
You can add whatever attributes you want to your form classes. Maybe you want to save a title, or a particular navigation widget. You could even save bits of text, or retrieve them from the database. Sometimes doing it this way would be the wrong way. But it's your form, your choice. In the right circumstances, it might provide a way to keep code out of your templates and simplify your controllers.
package MyApp::Form::Base;
use Moose;
extends 'HTML::FormHandler::Model::DBIC';
has 'title' => ( isa => 'Str', is => 'rw' );
has 'nav_bar' => ( isa => 'Str', is => 'rw' );
sub summary
{
my $self = shift;
my $schema = $self->schema;
my $text = $schema->resultset('Summary')->find( ... )->text;
return $text;
}
1;
Then:
package MyApp::Form::Whatsup;
use Moose;
extends 'MyApp::Form::Base';
has '+title' => ( default => 'This page is an example of what to expect...' );
has '+nav_bar' => ( default => ... );
...
1;
And in the template:
<h1>[% form.title %]</h1>
[% form.nav_bar %]
<p><b>Summary: </b>[% form.summary %]</p>
Or you can make these customizations Moose roles.
package MyApp::Form::Role::Base;
use Moose::Role;
...
package MyApp::Form::Whatsup;
use Moose;
with 'MyApp::Form::Role::Base';
...
Split up your forms into reusable pieces
A person form:
package Form::Person;
use HTML::FormHandler::Moose;
extends 'HTML::FormHandler';
has_field 'name';
has_field 'telephone';
has_field 'email' => ( type => 'Email' );
sub validate_name {
....
}
no HTML::FormHandler::Moose;
1;
An address form:
package Form::Address;
use HTML::FormHandler::Moose;
extends 'HTML::FormHandler';
has_field 'street';
has_field 'city';
has_field 'state' => ( type => 'Select' );
has_field 'zip' => ( type => '+Zip' );
sub options_state {
...
}
no HTML::FormHandler::Moose;
1;
A form that extends them both:
package Form::Member;
use Moose;
extends ('Form::Person', 'Form::Address');
no Moose;
1;
Or if you don't need to use the pieces of your forms as forms themself, you can use roles;
package Form::Role::Address;
use HTML::FormHandler::Moose::Role;
has_field 'street';
has_field 'city';
has_field 'state' => ( type => 'Select' );
has_field 'zip' => ( type => '+Zip' );
sub options_state {
...
}
no HTML::FormHandler::Moose::Role;
1;
You could make roles that are collections of validations:
package Form::Role::Member;
use Moose::Role;
sub check_zip {
...
}
sub check_email {
...
}
1;
And if the validations apply to fields with different names, specify the 'validate_meth' on the fields:
with 'Form::Role::Member';
has_field 'zip' => ( type => 'Integer', validate_meth => 'check_zip' );
Access a user record in the form
You might need the user_id to create specialized select lists, or do other form processing. Add a user_id attribute to your form:
has 'user_id' => ( isa => 'Int', is => 'rw' );
Then pass it in when you process the form:
$form->process( item => $item, params => $c->req->parameters, user_id = $c->user->user_id );
AUTHOR
Gerda Shank, gshank@cpan.org
COPYRIGHT
This library is free software, you can redistribute it and/or modify it under the same terms as Perl itself.