File: Cookbook.pod

package info (click to toggle)
libhtml-formfu-perl 2.01000-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 4,116 kB
  • ctags: 828
  • sloc: perl: 12,478; makefile: 7; sql: 5
file content (586 lines) | stat: -rw-r--r-- 16,347 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
=head1 NAME

HTML::FormFu::Manual::Cookbook - Cooking with HTML::FormFu

=head1 DESCRIPTION

Miscellaneous useful recipes for use with HTML::FormFu

=head1 GETTING STARTED

Some useful info for beginners.

=head2 Default search paths for config files

The current working directory (C<cwd>) (see L<HTML::FormFu/"load_config_file">).

If you're using the C<FormConfig> action attribute from 
L<Catalyst::Controller::HTML::FormFu>, forms should be saved in C<root/forms>. 
See L<Catalyst::Controller::HTML::FormFu/SYNOPSIS> and 
L<Catalyst::Controller::HTML::FormFu/config_file_path> for further details.

=head2 YAML

Most examples given in the L<HTML::FormFu> documentation use L<YAML> syntax. 
You can use any configuration file type supported by L<Config::Any>, but 
this author's preferred format is YAML.

A form can be populated by a config file by calling 
L<HTML::FormFu/load_config_file> with the filename as an argument. The 
config file is converted to a perl data-structure, and then passed to 
L<HTML::FormFu/populate>.

The config file must contain a hash-ref, with the keys corresponding to 
form method-names, and the values being the method arguments. For example, 
the following are equivalent:

    ---
    auto_fieldset: 1
    elements:
      - name: foo
      - name: bar
    
    # the above YAML is equivalent to the following perl code
    
    $form->auto_fieldset(1);
    
    $form->elements([
        { name => 'foo' },
        { name => 'bar' },
    ]);

When writing your config file, remember that perl hashes are unordered and 
cannot have multiple keys with the same name. 

See L<HTML::FormFu/load_config_file> and L<HTML::FormFu/populate> for 
more details.

See L<http://www.yaml.org/spec/> for the YAML specification.

=head1 BUILDING A FORM

=head2 Quick single-file prototypes

You can run the following script to quickly view a form's markup - replace 
the contents of the C<__DATA__> section with your own YAML config.

    #!/usr/bin/perl
    use strict;
    use warnings;
    use HTML::FormFu;
    use YAML::XS qw( LoadFile );
    
    my $form = HTML::FormFu->new;
    my $data = LoadFile(\*DATA);
    
    $form->populate($data);
    
    print $form;
    
    __DATA__
    ---
    auto_fieldset: 1
    elements:
      - type: Text
        name: foo

=head2 Unsupported HTML tags

You can use the L<HTML::FormFu::Element::Block> element, and set
the L<tag|HTML::FormFu::Element::Block/tag> to create any arbitrary pair of 
tags.

    ---
    elements:
      - type: Block
        tag: span
        content_xml: "<b>Hi!</b>"

You can use L<HTML::FormFu::Element::Block/content>, 
L<HTML::FormFu::Element::Block/content_xml> or 
L<HTML::FormFu::Element::Block/content_loc> to add any content you wish, or 
use L<HTML::FormFu::Element::Block/element> to add elements.

=head1 Application-wide default values

You can automatically set defaults using L<HTML::FormFu/default_args>, 
and if you set this in a L<Catalyst> application config file, it'll take
effect throughout your entire application, for example:

    myapp.yml
    ---
    'Controller::HTML::FormFu':
      constructor:
        default_args:
          elements:
            Textarea:
              rows: 10

=head1 MODIFYING A FORM

=head2 Insert a new field before existing form fields

See L<HTML::FormFu/insert_before> and L<HTML::FormFu/insert_after>.

    my $fieldset = $form->get_element({ type => 'Fieldset' });
    
    $fieldset->insert_before(
        $form->element(\%specs),
        $form->get_field($name)
    );

Another way to approach the problem is to use multiple config files, and 
decide which to load at runtime:

    # user_edit.yml
    ---
    elements:
      - type: Text
        name: email

    # user_username.yml
    ---
    elements:
      - type: Text
        name: username

     # user_register.yml
     ---
     load_config_file:
      - user_username.yml
      - user_edit.yml

    # create a user edit form, with only the email field
    
    $form->load_config_file( 'user_edit.yml' );
    
    # create a user registration form with username and email fields
    
    $form->load_config_file( 'user_register.yml' );

=head2 Form and Field attributes

You can add any arbitrary attributes to a form with 
L<HTML::FormFu/attributes>, or to any element with 
L<HTML::FormFu::Element/attributes>.

    ---
    attributes_xml:
      onsubmit: "js_function()"
    elements:
      - type: Text
        name: foo
        attributes_xml:
          onchange: "js_function()"

=head1 FORM VALIDATION

=head2 Check valid dates

Use L<HTML::FormFu::Inflator::DateTime>. When the inflator is processed, it
will try to create a DateTime object. An error will be returned if the 
supplied values do not make a valid date.

=head2 Check valid URI / URLs

See L<HTML::FormFu::Element::URL> or L<HTML::FormFu::Constraint::Regex>.

=head2 Implement a custom constraint / validator

If L<HTML::FormFu::Constraint::Callback> or 
L<HTML::FormFu::Validator::Callback> isn't sufficient for your needs, you 
can create your own class that inherits from L<HTML::FormFu::Constraint> or 
L<HTML::FormFu::Validator>, respectively.

It should implement a C<validate_value> method, which returns true is the 
value is valid, or false otherwise.

    package My::Custom::Validator;
    use Moose;
    extends 'HTML::FormFu::Validator';
    
    sub validate_value {
      my ( $self, $value, $params ) = @_;
      
      return 1 if value_is_valid( $value );
      
      return;
    }
    
    1;

Then add your custom validator to the form:

    ---
    elements:
      - type: Text
        name: foo
        validators:
          - '+My::Custom::Validator'

=head2 Constrain one form field based on the value of another

For example, you have a radiogroup and several text fields, with different text
fields being required depending on the value of the radiogroup.

This is achieved using the C<when> attribute of a constraint:

    constraints:
      - type: Length
        min: 8
        when:
          field: bar
          values: [ 1, 3, 5 ]

In the above example, the Length constraint is only processed when the form
field named "bar" has a value of either 1, 3 or 5.

You can also test for a negative condition using the C<not> attribute:

    constraints:
      - type: Length
        min: 8
        when:
          field: bar
          values: [ 1, 3, 5 ]
          not: 1

Now the constraint will be processed only if the value of field "bar" is NOT 1,
3 or 5.

Note: if you rely on the value of a checkbox for a when-restricted
contraint, you might want to consider setting C<default_empty_value> for
that checkbox. Take a look at L<HTML::FormFu::Role::Element::Field> to learn more.

Please read L<HTML::FormFu::Constraint> for futher information.

=head2 Constrain one form field based on the return value of a callback

You can use the C<when> attribute of a constraint also to decide using a
callback if the constraint should be applied.

For instance, the following (code) example shows a constraint being applied
only if the value of another field contains a pattern

    my $apply_if_pattern = sub {
        my $params = shift;
        return 1 if $params->{other_field} =~ m/\A ice_cream \z/xms;
        return 0;
    };

    $field->{constraints} = {
        type    => 'Required',
        when    => {
            callback    => $apply_if_pattern,
        }
    }


Please read L<HTML::FormFu::Constraint> for futher information.

=head1 HTML MARKUP

=head2 Indented HTML

Use L<HTML::FormFu::OutputProcessor::Indent>:

    ---
    output_processors:
      - Indent

=head2 Add a blank div (e.g. for AJAX purposes)

Simply add a Block element in the relevant place, it defaults to a C<DIV> 
tag.

    ---
    elements:
      - type: Text
        name: user
      
      - type: Block
        id: foo
      
      - type: Text
        name: email

=head1 DISPLAY

=head2 Custom error messages

If you want to display an error message due to an error in your own code, 
such as a database check; something which isn't implemented as a 
L<Constraint|HTML::FormFu::Constraint> or 
L<Validator|HTML::FormFu::Validator>; you can use a
L<Callback Constraint|HTML::FormFu::Constraint::Callback>.

If you don't provide your own callback routine, the default callback will 
always pass, regardless of user input.

You can take advantage of this by setting 
L<force_errors|HTML::FormFu/force_errors>, to display its error message 
when needed.

Example config:

    ---
    elements:
      - type: Text
      - name: email
      - constraints:
        type: Callback
        message: 'Email address already in use'

Example usage:

    if ( $@ =~ m/duplicate entry for key 'email'/i ) {
        
        $form->get_field('email')
             ->get_constraint({ type => 'Callback' })
             ->force_errors(1);
        
        $form->process;
        # then redisplay the form as normal
    }

=head2 Highlight required fields (or fields with certain types of constraint)

This can be achieved using the form's C<auto_constraint_class> method:

    $form->auto_constraint_class( 'constraint_%t' );

The container divs around any form field with a constraint will then have extra
CSS classes added, which indicate the type of constraint and allow you to apply
appropriate styling with CSS:

    /* change background of labels for fields with a Required constraint */
    fieldset .constraint_required label {
        background: #f00;
    }

This technique can also be used to add content before or after the fields in
question (note this will not work in older browsers with more limited CSS
support such as IE6):

    /* add an asterisk at the end of the label for required fields */
    fieldset .constraint_required label:after {
        content: '*'
    }

=head2 Add a popup hint to a field

Most display a tooltip when a user hovers their mouse pointer over an HTML
element with a "title" tag.
Aural browsers may try to turn the content into speech.
You can take advantage of this behaviour to provide a hint to the user about 
how to complete a form field.

    elements:
      - type: URL
        name: url
        label: Website
        title: 'Must start with http:// or https://'

The above will provide a hint when the "url" field receives focus. 
Or you could provide the hint for the container tag around both field and label:

    elements:
      - type: URL
        name: url
        label: Website
        container_attributes:
            title: 'Must start with http:// or https://'

=head2 Display filtered values

If you have a Filter on a field, such as L<HTML::FormFu::Filter::Whitespace> 
to strip leading / trailing whitespace, then if you redisplay the form the 
field is normally populated with the value the user originally entered.

If you would like the field to contain the filtered value, use 
L<HTML::FormFu/render_processed_value>.

=head2 Multiple forms using Catalyst::Controller::HTML::FormFu

Sometimes you need to display multiple forms on a single page. If you
try to use FormConfig on several actions in a chain, or similar, they
all use C<< $c->stash->{form} >> to store the form, hence you only get the last
form.

One way to work around such problems is to do a little of the work yourself:

In this example we have a login_form that we want on every page

    # root/forms/login.yml:
    ---
        indicator: username
        elements:
            -
                type: Text
                name: username
                constraints:
                    - Required
    ...

We also have an edit-form

    # root/forms/foo/edit.yml
    ---
        indicator: foo
        elements:
        -
            type: Text
            name: foo
            constraints:
                - Required
    ...

In this example, we want the login form to appear on every page, so
we load this in the top-most auto action:

    package MyApp::Controller::Root;

    BEGIN { extends 'Catalyst::Controller::HTML::FormFu'; }

    sub auto : Private {
        my ($self, $c) = @_;

        # We want to utilize a lot of the magic that the controller
        # gives us, so therefore we call $self->form like this

        my $login_form = $self->form;
        $login_form->load_config_file('login.yml');

        # Notice how we put it into another stash var, not 'form'
        $c->stash->{login_form} = $login_form;
        unless ($c->user_exists) {

            $login_form->process();

            if ($login_form->submitted_and_valid) {

                # Since we set indicator, we should only end up here if we
                # have a username in the form
                $c->authenticate({
                    username => $login_form->param_value('username'),
                    password => $login_form->param_value('password'),
                });
            }

        }
    }


Any other page that wants to load another form, can now do so freely:

    package MyApp::Controller::Foo;

    sub edit : Local FormConfig {
        my ( $self, $c ) = @_;

        my $form = $c->stash->{form};
        if ($form->submitted_and_valid) {
            # Do whatever you want with it :p
        }
    }

In the view we now have two stash-variables:

In F<root/foo/edit.tt>:
    [% login_form %]
    <h2>edit</h2>
    [% form %]

=head1 ADVANCED CUSTOMISATION

=head2 Installing the TT templates

It only makes sense to use the template files if you plan on customising
them, as the default C<string> render-method is faster.

As of C<HTML::FormFu v1.00>, L<TT|Template> is no longer listed a required
prerequisite - so you'll need to install it manually if you with to use the
template files.

If you're using the L<Catalyst> web framework, install 
L<Catalyst::Controller::HTML::FormFu> and run the following command:

    $ script/myapp_create.pl HTML::FormFu

This will create a directory, C<root/formfu>, containing the HTML::FormFu 
template files.

If you extend L<Catalyst::Controller::HTML::FormFu> and you don't set
HTML::FormFu's INCLUDE_PATH yourself, it will automatically be set 
to C<root/formfu> if that directory exists.

If you're not using L<Catalyst>, you can create the template files by 
running the following command:

      $ html_formfu_deploy.pl <target-directory>

Take note that if you choose to customise your own copy of HTML::FormFu's 
template files, you'll need to keep track of the C<Changes> file, when 
updating HTML::FormFu, so that you can update your own templates if the 
core templates are updated.

=head1 PERFORMANCE

=head2 Catalyst::Plugin::StackTrace

If you're using L<Catalyst::Plugin::StackTrace>, make sure you're using at
least version C<0.09> - earlier versions had performance problems with 
C<HTML::FormFu>.

=head2 Template::Alloy

You can also use L<Template::Alloy> instead of 
L<Template::Toolkit|Template>, it's mostly compatible, and in many cases 
provides a reasonable speed increase. You can do this either by setting the 
C<HTML_FORMFU_TEMPLATE_ALLOY> environment variable to a true value, or by 
passing C<TEMPLATE_ALLOY> to L<HTML::FormFu/tt_args>:

    tt_args:
      TEMPLATE_ALLOY: 1
      COMPILE_DIR: /tmp
      COMPILE_PERL: 1

Template::Alloy's caching is off by default. Switch it on by setting either 
C<COMPILE_EXT> or C<COMPILE_DIR>. If you're running under a persistent 
environment such as modperl or fastcgi, you should also set C<COMPILE_PERL> 
to compile the cached templates down to perl code.

Of cource, if you wish you can still use L<Template::Toolkit|Template> to 
process your own application templates, letting L<Template::Alloy> process 
just the HTML::FormFu templates.

=head2 HTML:FormFu::Preload

To reduce the runtime for each form that uses a previously unused
element or processor - at the expense of greater memory usage - you 
can preload all FormFu modules - this is only recommended for persistent 
environments such as modperl or fastcgi:

    use HTML::FormFu::Preload;

=head1 FAQs

=head2 Force an element to always have a certain value

See the following:

L<HTML::FormFu::Role::Element::Field/"retain_default">, 
L<HTML::FormFu::Role::Element::Field/"force_default">

=head1 AUTHORS

Will Hawes C<wdhawes@gmail.com>

Carl Franks C<cfranks@cpan.org>

=head1 COPYRIGHT

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

=cut