HTML::...okbook(3pm)
π HTML::FormFu::Manual::Cookbook π
okay bestie, so this is HTML::FormFu::Manual::Cookbook β it lives in the User Contributed Perl Documentation section of the manual (the 3pm part, iykyk). It's basically the group chat of FormFu recipes. Grab a snack, we're cooking π
πΈ NAME πΈ
HTML::FormFu::Manual::Cookbook β Cooking with HTML::FormFu π©βπ³β¨
πΈ VERSION πΈ
version 2.07 β the numbers, they're giving current π
πΈ DESCRIPTION πΈ
A lil grab-bag of miscellaneous useful recipes for using HTML::FormFu. That's it, that's the description. Short king energy, but the recipes below are iconic.
π¦ GETTING STARTED π¦
Some genuinely useful info for beginners. No gatekeeping here, we're all just vibing with forms π
β¨ Default search paths for config files β¨
The current working directory (aka cwd) β peep "load_config_file" in HTML::FormFu for the deets.
If you're using the FormConfig action attribute from Catalyst::Controller::HTML::FormFu, then your forms should be saved in root/forms. Want more? Go read "SYNOPSIS" in Catalyst::Controller::HTML::FormFu and "config_file_path" in Catalyst::Controller::HTML::FormFu. Slay π
β¨ YAML β¨
Most examples in the HTML::FormFu docs use YAML syntax. You can use any configuration file type that Config::Any supports, but the author's preferred format is β drumroll β YAML. Respect the author's taste, it's valid π«Ά
You can populate a form from a config file by calling "load_config_file" in HTML::FormFu with the filename as an argument. That config file gets converted into a perl data-structure, and then it's handed off to "populate" in HTML::FormFu. Cute pipeline, no notes.
The config file must contain a hash-ref, where the keys match up to the form's method-names, and the values are the method arguments. For example, these two are literally the same thing:
---
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 you're writing your config file, remember: perl hashes are unordered and cannot have multiple keys with the same name. That's just the rules, bestie.
See "load_config_file" in HTML::FormFu and "populate" in HTML::FormFu for more details.
And if you want the YAML spec itself: <http://www.yaml.org/spec/> β go off π
π BUILDING A FORM π
β¨ Quick single-file prototypes β¨
You can run the script below to quickly peep a form's markup β just swap out whatever's inside the "__DATA__" section with your own YAML config. Prototype era π
#!/usr/bin/perl
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
β¨ Unsupported HTML tags β¨
Got a tag FormFu doesn't natively do? Use the HTML::FormFu::Element::Block element and set the tag to create any arbitrary pair of tags you want. Main character energy, honestly.
---
elements:
- type: Block
tag: span
content_xml: "<b>Hi!</b>"
You can use "content" in HTML::FormFu::Element::Block, "content_xml" in HTML::FormFu::Element::Block or "content_loc" in HTML::FormFu::Element::Block to add whatever content your heart desires, or use "element" in HTML::FormFu::Element::Block to add elements. So many options, all of them valid π¦
π Application-wide default values π
You can set defaults automatically with "default_args" in HTML::FormFu β and if you set this in a Catalyst application config file, it'll take effect across your entire application. It's giving "set it and forget it", fr fr:
myapp.yml
---
'Controller::HTML::FormFu':
constructor:
default_args:
elements:
Textarea:
rows: 10
π MODIFYING A FORM π
β¨ Insert a new field before existing form fields β¨
Look at "insert_before" in HTML::FormFu and "insert_after" in HTML::FormFu. They do exactly what they say on the tin.
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 ones to load at runtime. Honestly? Obsessed with this approach.
# 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' );
β¨ Form and Field attributes β¨
You can slap any arbitrary attributes onto a form with "attributes" in HTML::FormFu, or onto any element with "attributes" in HTML::FormFu::Element. Go feral, the attributes are yours π
---
attributes_xml:
onsubmit: "js_function()"
elements:
- type: Text
name: foo
attributes_xml:
onchange: "js_function()"
π₯ FORM VALIDATION π₯
β¨ Check valid dates β¨
Use HTML::FormFu::Inflator::DateTime. When the inflator gets processed, it'll try to create a DateTime object. If the supplied values don't make a valid date, you get an error back. No fake dates on this form's watch π
β¨ Check valid URI / URLs β¨
Go look at HTML::FormFu::Element::URL or HTML::FormFu::Constraint::Regex. Pick your fighter π₯
β¨ Implement a custom constraint / validator β¨
If HTML::FormFu::Constraint::Callback or HTML::FormFu::Validator::Callback isn't enough for your needs, you can absolutely build your own class. Just inherit from HTML::FormFu::Constraint or HTML::FormFu::Validator respectively β like, whichever one you need.
It should implement a validate_value method, which returns true if the value is valid, or false otherwise. Simple as that, bestie.
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 β look at you go π¦
---
elements:
- type: Text
name: foo
validators:
- '+My::Custom::Validator'
β¨ Constrain one form field based on the value of another β¨
Say you've got a radiogroup and several text fields, and which text fields are required depends on what the radiogroup is set to. Very "it depends" of you, and honestly same.
You do this with the when attribute of a constraint:
constraints:
- type: Length
min: 8
when:
field: bar
values: [ 1, 3, 5 ]
In the example above, the Length constraint is only processed when the form field named bar has a value of either 1, 3 or 5. Periodt.
You can also test for a negative condition using the not attribute:
constraints:
- type: Length
min: 8
when:
field: bar
values: [ 1, 3, 5 ]
not: 1
Now the constraint only gets processed if the value of field bar is NOT 1, 3 or 5. Sneaky little inversion, love that for you π
π Note: if you rely on the value of a checkbox for a when-restricted constraint, you might wanna consider setting"default_empty_value"for that checkbox. Go peek atHTML::FormFu::Role::Element::Fieldto learn more β it's worth it, no cap.
Please read HTML::FormFu::Constraint for further information. It's the source of truth, bestie π
β¨ Constrain one form field based on the return value of a callback β¨
You can also use the when attribute of a constraint to decide β via a callback β whether the constraint should even be applied. The callback is the one in charge now π
For instance, this (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 HTML::FormFu::Constraint for further information. Again. It's that important π
π HTML MARKUP π
β¨ Indented HTML β¨
Use HTML::FormFu::OutputProcessor::Indent β your markup will be all neat and pretty, it's giving organised π
---
output_processors:
- Indent
β¨ Add a blank div (e.g. for AJAX purposes) β¨
Just drop a Block element in the relevant place β it defaults to a DIV tag, no extra config needed. Chef's kiss π¨βπ³π
---
elements:
- type: Text
name: user
- type: Block
id: foo
- type: Text
name: email
π DISPLAY π
π Custom error messages
okay bestie, so sometimes you need to throw an error that comes from your own code β like a database check, or literally anything that isn't already a Constraint or Validator. The move here? A Callback Constraint. β¨
And here's the tea: if you don't give it your own callback routine, the default callback will always pass, no matter what the user typed. So you can weaponise that β set force_errors and its error message shows up whenever you need it. π
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
}
πΈ Highlight required fields (or fields with certain types of constraint)
Bestie, you can pull this off with the form's auto_constraint_class method:
$form->auto_constraint_class( 'constraint_%t' );
After that, every container div wrapping a form field that has a constraint gets extra CSS classes β which tell you what kind of constraint it is and let you style it however you want with CSS:
/* change background of labels for fields with a Required constraint */
fieldset .constraint_required label {
background: #f00;
}
And you can also use this trick to add content before or after the fields in question β though heads up, this won't work in older browsers with more limited CSS support, like IE6:
/* add an asterisk at the end of the label for required fields */
fieldset .constraint_required label:after {
content: '*'
}
π¦ Add a popup hint to a field
Most browsers show a tooltip when a user hovers over an HTML element that has a title tag. Aural browsers might even try to turn that content into speech. π£οΈ You can totally use this to give the user a hint about how to fill in a form field.
elements:
- type: URL
name: url
label: Website
title: 'Must start with http:// or https://'
The above gives a hint whenever the url field gets focus. Or, if you'd rather, you can put the hint on the container tag that wraps both the field and the label:
elements:
- type: URL
name: url
label: Website
container_attributes:
title: 'Must start with http:// or https://'
π Display filtered values
If a field has a Filter on it β say, HTML::FormFu::Filter::Whitespace stripping leading / trailing whitespace β then when you redisplay the form, the field normally gets repopulated with whatever the user originally typed.
If you'd prefer the field to hold the filtered value instead, just use render_processed_value in HTML::FormFu. β¨
π Multiple forms using Catalyst::Controller::HTML::FormFu
Sometimes you need multiple forms on a single page. And if you try to use FormConfig on several actions in a chain, or something similar β plot twist β they all use $c->stash->{form} to store the form, so you only end up with the last one. π
One way around this 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 root/foo/edit.tt:
[% login_form %]
<h2>edit</h2>
[% form %]
π ADVANCED CUSTOMISATION π
π§ Installing the TT templates
Real talk: it only makes sense to use the template files if you're actually planning on customising them, because the default string render-method is faster. β‘
As of HTML::FormFu v1.00, TT is no longer listed as a required prerequisite β so you'll need to install it manually if you want to use the template files. (Yes, "if you with to" β that's the original's typo, we keep the lore. π)
If you're using the Catalyst web framework, install Catalyst::Controller::HTML::FormFu and run this command:
$ script/myapp_create.pl HTML::FormFu
This creates a directory, root/formfu, containing the HTML::FormFu template files. π
If you extend Catalyst::Controller::HTML::FormFu and you don't set HTML::FormFu's INCLUDE_PATH yourself, it'll automatically be set to root/formfu β but only if that directory exists.
If you're not using Catalyst, you can create the template files by running this command instead:
$ html_formfu_deploy.pl <target-directory>
Take note, bestie: if you choose to customise your own copy of HTML::FormFu's template files, you'll need to keep track of the Changes file whenever you update HTML::FormFu, so you can update your own templates if the core templates get updated. π
β PERFORMANCE β
π₯ Catalyst::Plugin::StackTrace
If you're using Catalyst::Plugin::StackTrace, make sure you're on at least version 0.09 β earlier versions had performance problems with HTML::FormFu. No cap.
π Template::Alloy
You can also use Template::Alloy instead of Template::Toolkit! It's mostly compatible, and in many cases gives a reasonable speed increase. ποΈ Do it either by setting the HTML_FORMFU_TEMPLATE_ALLOY environment variable to a true value, or by passing TEMPLATE_ALLOY to tt_args in HTML::FormFu:
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 COMPILE_EXT or COMPILE_DIR. And if you're running under a persistent environment like modperl or fastcgi, you should also set COMPILE_PERL to compile the cached templates down to perl code.
Of cource β yes, that's the original spelling, iconic β if you wish, you can still use Template::Toolkit to process your own application templates, and let Template::Alloy handle just the HTML::FormFu templates. π«Ά
π HTML:FormFu::Preload
To reduce the runtime for each form that uses a previously unused element or processor β at the cost 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;
π FAQs π
π Force an element to always have a certain value
See the following:
retain_default in HTML::FormFu::Role::Element::Field, force_default in HTML::FormFu::Role::Element::Field
β¨ AUTHORS β¨
Will Hawes wdhawes@gmail.com
Carl Franks cfranks@cpan.org
π COPYRIGHT π
This document is free, you can redistribute it and/or modify it under the same terms as Perl itself.
πΈ AUTHOR πΈ
Carl Franks <cpan@fireartist.com>
π COPYRIGHT AND LICENSE π
This software is copyright (c) 2018 by Carl Franks.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
and that's the whole page, bestie!! π«Ά this one's from perl v5.28.1, dated 2019-01-12 β yes it's giving vintage, but it's still serving HTML::...okbook(3pm) realness. go build something iconic with it π
β¨
π¦ where this page comes from
- debian bookworm: libhtml-formfu-perl 2.07000-1 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu jammy: libhtml-formfu-perl 2.07000-1 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu noble: libhtml-formfu-perl 2.07000-1 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms