=head1 NAME

HTML::FormHandler::Manual::Cookbook - FormHandler use recipes

=head1 SYNOPSIS

Collection of use recipes for L<HTML::FormHandler>

=head2 Dynamically change the active fields 

A common use case is for forms with some fields that should be displayed in
some circumstances and not in others. There are a number of ways to do this.
One way is to use the 'field_list' method:

   sub field_list {
      my $self = shift;
      my @fields;
      <build list of fields>
      return \@fields;
   }

This only happens at form construction time, however. Another method that
works is to define all of the possible fields in your form, and mark some
of them 'inactive';

   package MyApp::Variable::Form;
   use HTML::FormHandler::Moose;
   extends 'HTML::FormHandler';

   has_field 'foo';
   has_field 'bar' => ( inactive => 1 );
   1;

Then you can mark them active or inactive using a Moose method modifier on
'setup_form'.

   before 'setup_form' => sub {
      my $self = shift;
      if( <some condition> ) {
         $self->field('foo')->inactive(1);
         $self->field('bar')->inactive(0);
      }
      else {
         $self->field('foo')->inactive(0);
         $self->field('bar')->inactive(1);
      }
   }
   
=head2 Add custom attributes to FormHandler fields

If you want to add custom attributes to the FormHandler fields but don't want
to subclass all the fields, you can apply a role containing the new
attributes to L<HTML::FormHandler::Field> in your form. 

   package MyApp::Form::Custom;
   use HTML::FormHandler::Moose;
   extends 'HTML::FormHandler';
   use HTML::FormHandler::Field;

   # can't do this in BUILD because the base class BUILD fires before this one
   after 'BUILDARGS' => sub {
      my $fmeta = HTML::FormHandler::Field->meta;
      $fmeta->make_mutable;
      Moose::Util::apply_all_roles( $fmeta, ('MyApp::Field::Role::Custom'));
      $fmeta->make_immutable;
   };
   has_field 'bar';
   has_field 'foo';
   1;

   package MyApp::Field::Role::Custom;
   use Moose::Role;
   has 'my_extra_attribute => ( isa => 'Bool, is => 'rw' );
   1; 

If you want all your forms to use fields with the custom attributes, you
can create a base form object that applies the role, or turn the
code into a role and apply it in all of your forms.

=head2 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 ];
   }

=head2 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 by setting a flag to prevent database updates, performing
the pre-processing, and then updating the database yourself.

   has_field '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) validate (for the form or field)
   2) validate_model
   3) model_update

When the field is flagged 'writeonly', the value from the database will not
be used to fill in the form (put in the C<< $form->fif >> hash, or the
field C<< $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.

=head2 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';
   ...
   
=head2 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');

   use namespace::autoclean; 
   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 
'set_validate' on the fields:
  
   with 'Form::Role::Member';
   has_field 'zip' => ( type => 'Integer', set_validate => 'check_zip' );
 

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

=head2 Handle extra database fields

If there is another database field that needs to be updated when a row is 
created, add an attribute to the form, and then process it with 
C< before 'update_model' >.

In the form:

    has 'hostname' => ( isa => 'Int', is => 'ro' );

    before 'update_model' => sub {
       my $self = shift;
       $self->item->hostname( $self->hostname );
    };

Then just use an additional parameter when you create/process your form:

    $form->process( item => $item, params => $params, hostname => $c->req->host );

=head2 Record the user update 

Use the 'before' or 'after' method modifiers for 'update_model', to flag a record
as updated by the user, for example:

   before 'update_model' => sub {
      my $self = shift;
      $self->item->user_updated if $self->item;
   };

=head2 Doing cross validation in roles

In a role that handles a number of different fields, you may want to
perform cross validation after the individual fields are validated.
In the form you could use the 'validate' method, but that doesn't help
if you want to keep the functionality packaged in a role. Instead you
can use the 'after' method modifier on the 'validate' method:


   package MyApp::Form::Roles::DateFromTo;

   use HTML::FormHandler::Moose::Role;
   has_field 'date_from' => ( type => 'Date' );
   has_field 'date_to'   => ( type => 'Date' );

   after 'validate' => sub {
      my $self = shift;
      $self->field('date_from')->add_error('From date must be before To date')
         if $self->field('date_from')->value gt $self->field('date_to')->value;
   };

=head2 Changing required flag

Sometimes a field is required in one situation and not required in another.
You can use a method modifier before 'validate_form':

   before 'validate_form' => sub {
      my $self = shift;
      my $required = 0;
      $required = 1
         if( $self->params('field_name') eq 'something' ); 
      $self->field('some_field')->required($required);
   };

This happens before the fields contain input or values, so you would need to
look at the param value. If you need the validated value, it might be better
to do these sort of checks in the form's 'validate' routine.

   sub validate {
      my $self = shift;
      $self->field('dependent_field')->add_error("Field is required")
          if( $self->field('some_field')->value eq 'something' &&
              !$self->field('dependent_field')->has_value);
   }

In a Moose role you would need to use a method modifier instead.
  
   after 'validate' => sub { ... };

Don't forget the dependency list, which is used for cases where if any of one
of a group of fields has a value, all of the fields are required.

=head1 AUTHOR

Gerda Shank, gshank@cpan.org

=head1 COPYRIGHT

This library is free software, you can redistribute it and/or modify it under
the same terms as Perl itself.

=cut
