1HTML::FormHandler::ManuUasle:r:CCooonktbroiobku(t3e)d PeHrTlMLD:o:cFuomremnHtaantdiloenr::Manual::Cookbook(3)
2
3
4

NAME

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

VERSION

9       version 0.40068
10

SYNOPSIS

12       Manual Index
13
14       Collection of use recipes for HTML::FormHandler
15
16   No form file, no template file...
17       I had to create a tiny little form this week for admins to enter a
18       comment, and it seemed silly to have to create a form file and a
19       template file. I remembered that you can set the TT 'template' to a
20       string reference and not use a template at all, which is nice when
21       FormHandler will create the form HTML for you anyway.
22
23           sub comment : Chained('base_sub') PathPart('comment') Args(0) {
24               my ( $self, $c ) = @_;
25
26               my $form = HTML::FormHandler->new( field_list =>
27                   [ comment => { type => 'Text', size => 60 },
28                     submit => {type => 'Submit'} ] );
29               $form->process($c->req->params);
30               if ( $form->validated ) {
31                   $self->admin_log( $c, "Admin::Queue", "admin comment",
32                         $form->field('comment')->value );
33                   $c->flash( message => 'Comment added' );
34                   $c->res->redirect( $c->stash->{urilist}->{view} );
35               }
36               my $rendered_form = $form->render;
37               $c->stash( template => \$rendered_form );
38           }
39
40       This creates the form on the fly with a comment field and a submit
41       button, renders it using the default TT wrappers, then logs the
42       comment. No other files at all....
43
44       FormHandler isn't really necessary for validation here, but it does
45       make it possible to have a simple, standalone method.
46
47   Dynamically change the active fields
48       A common use case is for forms with some fields that should be
49       displayed in some circumstances and not in others. There are a number
50       of ways to do this.  One way is to use the 'field_list' method:
51
52          sub field_list {
53             my $self = shift;
54             my @fields;
55             <build list of fields>
56             return \@fields;
57          }
58
59       This only happens at form construction time, however. Another method
60       that works is to define all of the possible fields in your form, and
61       mark some of them 'inactive';
62
63          package MyApp::Variable::Form;
64          use HTML::FormHandler::Moose;
65          extends 'HTML::FormHandler';
66
67          has_field 'foo';
68          has_field 'bar' => ( inactive => 1 );
69          1;
70
71       Set to 'active' or 'inactive' on the 'process' call:
72
73          $form->process( params => $params, active => ['foo', 'bar'] );
74          ...
75          $form->process( params => $params, inactive => ['bar'] );
76
77       If you need to check some other state to determine whether or not a
78       field should be active, you can do that using a Moose method modifier
79       on 'set_active':
80
81          before 'set_active' => sub {
82             my $self = shift;
83             $self->active(['foo', bar']) if ( <some_condition> );
84          };
85
86       Fields set to active/inactive on the 'process' call are automatically
87       set back to inactive when the form is cleared, so there's no need to
88       reset.
89
90       If you want the fields activated for the life of an object, set active
91       on new:
92
93           my $form = MyApp::Form::User->new( active => ['opt_in', 'active']);
94
95   Add custom attributes to FormHandler fields
96       If you want to add custom attributes to the FormHandler fields but
97       don't want to subclass all the fields, you can apply a role containing
98       the new attributes to an HTML::FormHandler::Field in your form.
99
100       Use 'traits' on the individual fields to apply a role to field
101       instances.  Use the form attribute 'field_traits' to apply a role to
102       all field instances in the form.
103
104           package MyApp::Form::Test;
105           use HTML::FormHandler::Moose;
106           extends 'HTML::FormHandler';
107
108           has_field 'foo' => ( traits => ['MyApp::TraitFor::Test'] );
109           has '+field_traits' => ( default => sub { ['Some::Trait', 'Another::Trait'] } );
110
111       Or set the traits on new:
112
113           my $form = MyApp::Form::User->new( field_traits => ['MyApp::TraitFor::Test'] );
114           my $form = MyApp::Form::User->new(
115                    field_list => [ '+foo' => { traits => [...] } ]);
116
117       To apply the role to a field base class, use 'apply_traits' on that
118       class:
119
120           HTML::FormHandler::Field->apply_traits( 'Some::Test' );
121           HTML::FormHandler::Field::Text->apply_traits( 'Another::Trait' );
122
123   Select lists
124       If you want to set the default value of a select field to 0, you can
125       just use 'default' on the field:
126
127          has_field 'license' => ( default => 0 );
128
129       If there is logic involved, you can use a 'default_<field_name>'
130       method:
131
132          sub default_license {
133             my ( $self, $field, $item ) = @_;
134             return 0 unless $item && $item->license_id;
135             return $item->license_id;
136          }
137
138       If the table defining the choices for a select list doesn't include a
139       'no choice' choice, you can set 'empty_select' in your field if you are
140       using FormHandler rendering:
141
142          has_field 'subject_class' => ( type => 'Select',
143             empty_select => '--- Choose Subject Class ---' );
144
145       Or you can do in a template:
146
147          [% f = form.field('subject_class') %]
148          <select id="select_sc" name="[% f.name %]">
149            <option value="">--- Choose Subject Class---</option>
150            [% FOR option IN f.options %]
151              <option value="[% option.value %]"
152                 [% IF option.value == f.fif %]selected="selected"[% END %]>
153                 [% option.label | html %]</option>
154            [% END %]
155          </select>
156
157       You can create a custom select list in an 'options_' method:
158
159          sub options_country {
160             my $self = shift;
161             return unless $self->schema;
162             my @rows =
163                $self->schema->resultset( 'Country' )->
164                   search( {}, { order_by => ['rank', 'country_name'] } )->all;
165             return [ map { $_->digraph, $_->country_name } @rows ];
166          }
167
168   The database and FormHandler forms
169       If you have to process the input data before saving to the database,
170       and this is something that would be useful in other places besides your
171       form, you should do that processing in the DBIx::Class result class.
172
173       If the pre-processing is only relevant to HTML form input, you might
174       want to do it in the form by setting a flag to prevent database
175       updates, performing the pre-processing, and then updating the database
176       yourself.
177
178          has_field 'my_complex_field' => ( type => 'Text', noupdate => 1 );
179
180       The 'noupdate' flag is set in order to skip an attempt to update the
181       database for this field (it would not be necessary if the field doesn't
182       actually exist in the database...).  You can process the input for the
183       non-updatable field field in a number of different places, depending on
184       what is most logical.  Some of the choices are:
185
186          1) validate (for the form or field)
187          2) validate_model
188          3) update_model
189
190       When the field is flagged 'writeonly', the value from the database will
191       not be used to fill in the form (put in the "$form->fif" hash, or the
192       field "$field->fif"), but a value entered in the form WILL be used to
193       update the database.
194
195       If you want to enter fields from an additional table that is related to
196       this one in a 'single' relationship, you can use the DBIx::Class
197       'proxy' feature to create accessors for those fields.
198
199   Set up form base classes or roles for your application
200       You can add whatever attributes you want to your form classes. Maybe
201       you want to save a title, or a particular navigation widget. You could
202       even save bits of text, or retrieve them from the database.
203
204          package MyApp::Form::Base;
205          use Moose;
206          extends 'HTML::FormHandler::Model::DBIC';
207
208          has 'title' => ( isa => 'Str', is => 'rw' );
209          has 'nav_bar' => ( isa => 'Str', is => 'rw' );
210          has_block 'reg_header' => ( tag => 'fieldset', label => 'Registration form',
211              content => 'We take your membership seriously...' );
212
213          sub summary {
214             my $self = shift;
215             my $schema = $self->schema;
216             my $text = $schema->resultset('Summary')->find( ... )->text;
217             return $text;
218          }
219          1;
220
221       Then:
222
223          package MyApp::Form::Whatsup;
224          use Moose;
225          extends 'MyApp::Form::Base';
226
227          has '+title' => ( default => 'This page is an example of what to expect...' );
228          has '+nav_bar' => ( default => ... );
229          ...
230          1;
231
232       And in the template:
233
234          <h1>[% form.title %]</h1>
235          [% form.nav_bar %]
236          [% form.block('reg_header')->render %]
237          <p><b>Summary: </b>[% form.summary %]</p>
238
239       Or you can make these customizations Moose roles.
240
241          package MyApp::Form::Role::Base;
242          use Moose::Role;
243          ...
244
245          package MyApp::Form::Whatsup;
246          use Moose;
247          with 'MyApp::Form::Role::Base';
248          ...
249
250   Split up your forms into reusable pieces
251       An address field:
252
253          package Form::Field::Address;
254          use HTML::FormHandler::Moose;
255          extends 'HTML::FormHandler::Field::Compound';
256
257          has_field 'street';
258          has_field 'city';
259          has_field 'state' => ( type => 'Select', options_method => \&options_state );
260          has_field 'zip' => ( type => '+Zip' );
261
262          sub options_state {
263            ...
264          }
265
266          no HTML::FormHandler::Moose;
267          1;
268
269       A person form that includes an address field:
270
271          package Form::Person;
272          use HTML::FormHandler::Moose;
273          extends 'HTML::FormHandler';
274
275          has '+widget_name_space' => ( default => sub {['Form::Field']} );
276          has_field 'name';
277          has_field 'telephone';
278          has_field 'email' => ( type => 'Email' );
279          has_field 'address' => ( type => 'Address' );
280
281          sub validate_name {
282           ....
283          }
284
285          no HTML::FormHandler::Moose;
286          1;
287
288       Or you can use roles;
289
290          package Form::Role::Address;
291          use HTML::FormHandler::Moose::Role;
292
293          has_field 'street';
294          has_field 'city';
295          has_field 'state' => ( type => 'Select' );
296          has_field 'zip' => ( type => '+Zip' );
297
298          sub options_state {
299            ...
300          }
301
302          no HTML::FormHandler::Moose::Role;
303          1;
304
305       You could make roles that are collections of validations:
306
307          package Form::Role::Member;
308          use Moose::Role;
309
310          sub check_zip {
311             ...
312          }
313          sub check_email {
314             ...
315          }
316
317          1;
318
319       And if the validations apply to fields with different names, specify
320       the 'validate_method' on the fields:
321
322          with 'Form::Role::Member';
323          has_field 'zip' => ( type => 'Integer', validate_method => \&check_zip );
324
325   Access a user record in the form
326       You might need the user_id to create specialized select lists, or do
327       other form processing. Add a user_id attribute to your form:
328
329         has 'user_id' => ( isa => 'Int', is => 'rw' );
330
331       Then pass it in when you process the form:
332
333         $form->process( item => $item, params => $c->req->parameters, user_id => $c->user->user_id );
334
335   Handle extra database fields
336       If there is another database field that needs to be updated when a row
337       is created, add an attribute to the form, and then process it with "
338       before 'update_model' ".
339
340       In the form:
341
342           has 'hostname' => ( isa => 'Int', is => 'rw' );
343
344           before 'update_model' => sub {
345              my $self = shift;
346              $self->item->hostname( $self->hostname );
347           };
348
349       Then just use an additional parameter when you create/process your
350       form:
351
352           $form->process( item => $item, params => $params, hostname => $c->req->host );
353
354       Some kinds of DB relationships need to have primary keys which might be
355       more easily set in the update_model method;
356
357           sub update_model {
358               my $self = shift;
359               my $values = $self->values;
360               $values->{some_field}->{some_key} = 'some_value';
361               $self->_set_value($values);
362               $self->next::method;
363           }
364
365       If you need to access a database field in order to create the value for
366       a form field you can use a " default_* " method.
367
368           sub default_myformfield {
369               my ($self, $field, $item) = @_;
370               return unless defined $item;
371               my $databasefield =  $item->databasefield;
372               my $value = ... # do stuff
373               return $value;
374           }
375
376   Additional changes to the database
377       If you want to do additional database updates besides the ones that
378       FormHandler does for you, the best solution would generally be to add
379       the functionality to your result source or resultset classes, but if
380       you want to do additional updates in a form you should use an 'around'
381       method modifier and a transaction:
382
383         around 'update_model' => sub {
384             my $orig = shift;
385             my $self = shift;
386             my $item = $self->item;
387
388             $self->schema->txn_do( sub {
389                 $self->$orig(@_);
390
391                 <perform additional updates>
392             });
393         };
394
395   Doing cross validation in roles
396       In a role that handles a number of different fields, you may want to
397       perform cross validation after the individual fields are validated.  In
398       the form you could use the 'validate' method, but that doesn't help if
399       you want to keep the functionality packaged in a role. Instead you can
400       use the 'after' method modifier on the 'validate' method:
401
402          package MyApp::Form::Roles::DateFromTo;
403
404          use HTML::FormHandler::Moose::Role;
405          has_field 'date_from' => ( type => 'Date' );
406          has_field 'date_to'   => ( type => 'Date' );
407
408          after 'validate' => sub {
409             my $self = shift;
410             $self->field('date_from')->add_error('From date must be before To date')
411                if $self->field('date_from')->value gt $self->field('date_to')->value;
412          };
413
414   Changing required flag
415       Sometimes a field is required in one situation and not required in
416       another.  You can use a method modifier before 'validate_form':
417
418          before 'validate_form' => sub {
419             my $self = shift;
420             my $required = 0;
421             $required = 1
422                if( $self->params->{field_name} eq 'something' );
423             $self->field('some_field')->required($required);
424          };
425
426       This happens before the fields contain input or values, so you would
427       need to look at the param value. If you need the validated value, it
428       might be better to do these sort of checks in the form's 'validate'
429       routine.
430
431          sub validate {
432             my $self = shift;
433             $self->field('dependent_field')->add_error("Field is required")
434                 if( $self->field('some_field')->value eq 'something' &&
435                     !$self->field('dependent_field')->has_value);
436          }
437
438       In a Moose role you would need to use a method modifier instead.
439
440          after 'validate' => sub { ... };
441
442       Don't forget the dependency list, which is used for cases where if any
443       of one of a group of fields has a value, all of the fields are
444       required.
445
446   Supply an external coderef for validation
447       There are situations in which you need to use a subroutine for
448       validation which is not logically part of the form. It's possible to
449       pass in a context or other sort of pointer and call the routine in the
450       form's validation routine, but that makes the architecture muddy and is
451       not a clear separation of concerns.
452
453       This is an example of how to supply a coderef when constructing the
454       form that performs validation and can be used to set an appropriate
455       error using Moose::Meta::Attribute::Native::Trait::Code.  (Thanks to
456       Florian Ragwitz for this excellent idea...)
457
458       Here's the form:
459
460           package SignupForm;
461           use HTML::FormHandler::Moose;
462           extends 'HTML::FormHandler';
463
464           has check_name_availability => (
465               traits   => ['Code'],
466               isa      => 'CodeRef',
467               required => 1,
468               handles  => { name_available => 'execute', },
469           );
470
471           has_field 'name';
472           has_field 'email';
473
474           sub validate {
475               my $self = shift;
476               my $name = $self->value->{name};
477               if ( defined $name && length $name && !$self->name_available($name) ) {
478                   $self->field('name')->add_error('That name is taken already');
479               }
480           }
481           1;
482
483       And here's where the coderef is passed in to the form.
484
485           package MyApp::Signup;
486           use Moose;
487
488           has 'form' => ( is => 'ro', builder => 'build_form' );
489           sub build_form {
490               my $self = shift;
491               return SignupForm->new(
492                   {
493                       check_name_availability => sub {
494                           my $name = shift;
495                           return $self->username_available($name);
496                       },
497                   }
498               );
499
500           }
501           sub username_available {
502               my ( $self, $name ) = @_;
503               # perform some sort of username availability checks
504           }
505           1;
506
507   Example of a form with custom database interface
508       The default DBIC model requires that the form structure match the
509       database structure. If that doesn't work - you need to present the form
510       in a different way - you may need to fudge it by creating your own
511       'init_object' and doing the database updates in the 'update_model'
512       method.
513
514       Here is a working example for a 'family' object (equivalent to a 'user'
515       record') that has a relationship to permission type roles in a
516       relationship 'user_roles'.
517
518           package My::Form::AdminRoles;
519           use HTML::FormHandler::Moose;
520           extends 'HTML::FormHandler';
521
522           has 'schema' => ( is => 'ro', required => 1 );  # Note 1
523           has '+widget_wrapper' => ( default => 'None' ); # Note 2
524
525           has_field 'admin_roles' => ( type => 'Repeatable' ); # Note 3
526           has_field 'admin_roles.family'    => ( type => 'Hidden' ); # Note 4
527           has_field 'admin_roles.family_id' => ( type => 'PrimaryKey' ); # Note 5
528           has_field 'admin_roles.admin_flag' => ( type => 'Boolean', label => 'Admin' );
529
530           # Note 6
531           sub init_object {
532               my $self = shift;
533
534               my @is_admin;
535               my @is_not_admin;
536               my $active_families = $self->schema->resultset('Family')->search( { active => 1 } );
537               while ( my $fam = $active_families->next ) {
538                   my $admin_flag =
539                        $fam->search_related('user_roles', { role_id => 2 } )->count > 0 ? 1 : 0;
540                   my $family_name = $fam->name1 . ", " . $fam->name2;
541                   my $elem =  { family => $family_name, family_id => $fam->family_id,
542                        admin_flag => $admin_flag };
543                   if( $admin_flag ) {
544                       push @is_admin, $elem;
545                   }
546                   else {
547                       push @is_not_admin, $elem;
548                   }
549               }
550               # Note 7
551               # sort into admin flag first, then family_name
552               @is_admin = sort { $a->{family} cmp $b->{family} } @is_admin;
553               @is_not_admin = sort { $a->{family} cmp $b->{family} } @is_not_admin;
554               return { admin_roles => [@is_admin, @is_not_admin] };
555           }
556
557           # Note 8
558           sub update_model {
559               my $self = shift;
560
561               my $families = $self->schema->resultset('Family');
562               my $family_roles = $self->value->{admin_roles};
563               foreach my $elem ( @{$family_roles} ) {
564                   my $fam = $families->find( $elem->{family_id} );
565                   my $has_admin_flag = $fam->search_related('user_roles', { role_id => 2 } )->count > 0;
566                   if( $elem->{admin_flag} == 1 && !$has_admin_flag ) {
567                       $fam->create_related('user_roles', { role_id => 2 } );
568                   }
569                   elsif( $elem->{admin_flag} == 0 && $has_admin_flag ) {
570                       $fam->delete_related('user_roles', { role_id => 2 } );
571                   }
572               }
573           }
574
575       Note 1: This form creates its own 'schema' attribute. You could inherit
576       from HTML::FormHandler::Model::DBIC, but you won't be using its update
577       code, so it wouldn't add much.
578
579       Note 2: The form will be displayed with a template that uses 'bare'
580       form input fields, so 'widget_wrapper' is set to 'None' to skip
581       wrapping the form inputs with divs or table elements.
582
583       Note 3: This form consists of an array of elements, so there will be a
584       single Repeatable form field with subfields. If you wanted to use
585       automatic rendering, you would also need to create a 'submit' field,
586       but in this case it will just be done in the template.
587
588       Note 4: This field is actually going to be used for display purposes
589       only, but it's a hidden field because otherwise the information would
590       be lost when displaying the form from parameters. For this case there
591       is no real 'validation' so it might not be necessary, but it would be
592       required if the form needed to be re-displayed with error messages.
593
594       Note 5: The 'family_id' is the primary key field, necessary for
595       updating the correct records.
596
597       Note 6: 'init_object' method: This is where the initial object is
598       created, which takes the place of a database row for form creation.
599
600       Note 7: The entries with the admin flag turned on are sorted into the
601       beginning of the list. This is entirely a user interface choice.
602
603       Note 8: 'update_model' method: This is where the database updates are
604       performed.
605
606       The Template Toolkit template for this form:
607
608           <h1>Update admin status for members</h1>
609           <form name="adminroles" method="POST" action="[% c.uri_for('admin_roles') %]">
610             <input class="submit" name="submit" value="Save" type="submit">
611           <table border="1">
612             <th>Family</th><th>Admin</th>
613             [% FOREACH f IN form.field('admin_roles').sorted_fields %]
614                <tr>
615                <td><b>[% f.field('family').fif %]</b>[% f.field('family').render %]
616                [% f.field('family_id').render %]</td><td> [% f.field('admin_flag').render %]</td>
617                </tr>
618             [% END %]
619           </table>
620             <input class="submit" name="submit" value="Save" type="submit">
621           </form
622
623       The form is rendered in a simple table, with each field rendered using
624       the automatically installed rendering widgets with no wrapper
625       (widget_wrapper => 'None').  There are two hidden fields here, so what
626       is actually seen is two columns, one with the user (family) name, the
627       other with a checkbox showing whether the user has admin status. Notice
628       that the 'family' field information is rendered twice: once as a hidden
629       field that will allow it to be preserved in params, once as a label.
630
631       The Catalyst controller action to execute the form:
632
633           sub admin_roles : Local {
634               my ( $self, $c ) = @_;
635
636               my $schema = $c->model('DB')->schema;
637               my $form = My::Form::AdminRoles->new( schema => $schema );
638               $form->process( params => $c->req->params );
639               # re-process if form validated to reload from db and re-sort
640               $form->process( params => {}) if $form->validated;
641               $c->stash( form => $form, template => 'admin/admin_roles.tt' );
642               return;
643           }
644
645       Rather than redirect to some other page after saving the form, the form
646       is redisplayed.  If the form has been validated (i.e. the
647       'update_model' method has been run), the 'process' call is run again in
648       order to re-sort the displayed list with admin users at the top. That
649       could have also been done in the 'update_model' method.
650
651   A form that takes a resultset, with custom update_model
652       For updating a Repeatable field that is filled from a Resultset, and
653       not a relationship on a single row. Creates a 'resultset' attribute to
654       pass in a resultset. Massages the data into an array that's pointed to
655       by an 'employers' hash key, and does the reverse in the 'update_model'
656       method.  Yes, it's a kludge, but it could be worse. If you want to
657       implement a more general solution, patches welcome.
658
659           package Test::Resultset;
660           use HTML::FormHandler::Moose;
661           extends 'HTML::FormHandler::Model::DBIC';
662
663           has '+item_class' => ( default => 'Employer' );
664           has 'resultset' => ( isa => 'DBIx::Class::ResultSet', is => 'rw',
665                   trigger => sub { shift->set_resultset(@_) } );
666           sub set_resultset {
667               my ( $self, $resultset ) = @_;
668               $self->schema( $resultset->result_source->schema );
669           }
670           sub init_object {
671               my $self = shift;
672               my $rows = [$self->resultset->all];
673               return { employers => $rows };
674           }
675           has_field 'employers' => ( type => 'Repeatable' );
676           has_field 'employers.employer_id' => ( type => 'PrimaryKey' );
677           has_field 'employers.name';
678           has_field 'employers.category';
679           has_field 'employers.country';
680
681           sub update_model {
682               my $self = shift;
683               my $values = $self->values->{employers};
684               foreach my $row (@$values) {
685                   delete $row->{employer_id} unless defined $row->{employer_id};
686                   $self->resultset->update_or_create( $row );
687               }
688           }
689
690   Server-provided dynamic value for field
691       There are many different ways to provide values for fields. Default
692       values can be statically provided in the form with the 'default'
693       attribute on the field, with a default_<field_name> method in the form,
694       with an init_object/item, and with 'default_over_obj' if you have both
695       an item/init_object and want to provide a default.
696
697           has_field 'foo' => ( default => 'my_default' );
698           has_field 'foo' => ( default_over_obj => 'my_default' );
699           sub default_foo { 'my_default' }
700           ..
701           $form->process( init_object => { foo => 'my_default } );
702           $form->process( item => <object with $obj->foo method to provide default> );
703
704       If you want to change the default for the field at run time, there are
705       a number of options.
706
707       You can set the value in the init_object or item before doing process:
708
709           my $foo_value = 'some calculated value';
710           $form->process( init_object => { foo => $foo_value } );
711
712       You can use 'update_field_list' or 'defaults' on the 'process' call:
713
714           $form->process( update_field_list => { foo => { default => $foo_value } } );
715           -- or --
716           $form->process( defaults => { foo => $foo_value } );
717
718       You can set a Moose attribute in the form class, and set the default in
719       a default_<field_name> method:
720
721           package My::Form;
722           use HTML::FormHandler::Moose;
723           extends 'HTML::Formhandler';
724
725           has 'form_id' => ( isa => 'Str', is => 'rw' );
726           has_field 'foo';
727           sub default_foo {
728               my $self = shift;
729               return $self->form_id;
730           }
731           ....
732           $form->process( form_id => 'my_form', params => $params );
733
734       You can set a Moose attribute in the form class and set it in an
735       update_fields method:
736
737           sub update_fields {
738               my $self = shift;
739               $self->field('foo')->default('my_form');
740           }
741
742   Static form, dynamic field IDs
743       The problem: you have a form that will be used in multiple places on a
744       page, but you want to use a static form instead of doing 'new' for
745       each. You can pass a form name in on the process call and use
746       'html_prefix' in the form:
747
748          $form->process( name => '...', params => {} );
749
750       But the field 'id' attribute has already been constructed and doesn't
751       change.
752
753       Solution: apply a role to the base field class to replace the 'id'
754       getter for the 'id' attribute with a method which constructs the 'id'
755       dynamically. Since the role is being applied to the base field class,
756       you can't just use 'sub id', because the 'id' method defined by the
757       'id' attribute has precedence. So create an 'around' method modifier
758       that replaces it in the role.
759
760           package My::DynamicFieldId;
761           use Moose::Role;
762           around 'id' => sub {
763               my $orig = shift;
764               my $self = shift;
765               my $form_name = $self->form->name;
766               return $form_name . "." . $self->full_name;
767           };
768
769           package My::CustomIdForm;
770           use HTML::FormHandler::Moose;
771           extends 'HTML::FormHandler';
772
773           has '+html_prefix' => ( default => 1 );
774           has '+field_traits' => ( default => sub { ['My::DynamicFieldId'] } );
775
776           has_field 'foo';
777           has_field 'bar';
778
779   Create different field IDs
780       Use 'build_id_method' to give your fields a different format 'id':
781
782           package MyApp::CustomId;
783           use HTML::FormHandler::Moose;
784           extends 'HTML::FormHandler';
785
786           has '+update_field_list' => ( default =>
787               sub { { all => { build_id_method => \&custom_id } } } );
788           has_field 'foo' => ( type => 'Compound' );
789           has_field 'foo.one';
790           has_field 'foo.two';
791           has_field 'foo.three';
792           sub custom_id {
793               my $self = shift;
794               my $full_name = $self->full_name;
795               $full_name =~ s/\./_/g;
796               return $full_name;
797           }
798
799       The above method provides IDs of "foo_two" and "foo_three" instead of
800       "foo.two" and "foo.three".
801

AUTHOR

803       FormHandler Contributors - see HTML::FormHandler
804
806       This software is copyright (c) 2017 by Gerda Shank.
807
808       This is free software; you can redistribute it and/or modify it under
809       the same terms as the Perl 5 programming language system itself.
810
811
812
813perl v5.38.0                      2023-07H-T2M0L::FormHandler::Manual::Cookbook(3)
Impressum