This portion of the tutorial explores L<HTML::FormFu> and how it can be
used to manage forms, perform validation of form input, as well as save
-and restore data to/from the database. This was written using
-HTML::FormFu version 0.05001.
+and restore data to/from the database.
See L<Catalyst::Manual::Tutorial::09_AdvancedCRUD> for additional form
management options other than L<HTML::FormFu>.
+Source code for the tutorial in included in the F</home/catalyst/Final>
+directory of the Tutorial Virtual machine (one subdirectory per
+chapter). There are also instructions for downloading the code in
+L<Catalyst::Manual::Tutorial::01_Intro>.
+
=head1 HTML::FormFu FORM CREATION
This section looks at how L<HTML::FormFu> can be used to add additional
-functionality to the manually created form from Chapter 4.
+functionality to the manually created form from
+L<Chapter 4|Catalyst::Manual::Tutorial::04_BasicCRUD>.
=head2 Inherit From Catalyst::Controller::HTML::FormFu
-First, change your C<lib/MyApp/Controller/Books.pm> to inherit from
+First, change your F<lib/MyApp/Controller/Books.pm> to inherit from
L<Catalyst::Controller::HTML::FormFu> by changing the C<extends> line
from the default of:
Don't forget to add:
+ requires 'HTML::FormFu';
requires 'Catalyst::Controller::HTML::FormFu';
+ requires 'HTML::FormFu::Model::DBIC';
to your C<Makefile.PL>.
+
=head2 Add Action to Display and Save the Form
-Open C<lib/MyApp/Controller/Books.pm> in your editor and add the
+Open F<lib/MyApp/Controller/Books.pm> in your editor and add the
following method:
=head2 formfu_create
-
+
Use HTML::FormFu to create a new book
-
+
=cut
-
+
sub formfu_create :Chained('base') :PathPart('formfu_create') :Args(0) :FormConfig {
my ($self, $c) = @_;
-
+
# Get the form that the :FormConfig attribute saved in the stash
my $form = $c->stash->{form};
-
+
# Check if the form has been submitted (vs. displaying the initial
# form) and if the data passed validation. "submitted_and_valid"
# is shorthand for "$form->submitted && !$form->has_errors"
my $book = $c->model('DB::Book')->new_result({});
# Save the form data for the book
$form->model->update($book);
- # Set a status message for the user
- $c->flash->{status_msg} = 'Book created';
- # Return to the books list
- $c->response->redirect($c->uri_for($self->action_for('list')));
+ # Set a status message for the user & return to books list
+ $c->response->redirect($c->uri_for($self->action_for('list'),
+ {mid => $c->set_status_msg("Book created")}));
$c->detach;
} else {
# Get the authors from the DB
# Add the authors to it
$select->options(\@authors);
}
-
+
# Set the template
$c->stash(template => 'books/formfu_create.tt2');
}
=head2 Create a Form Config File
-Although C<HTML::FormFu> supports any configuration file handled by
+Although L<HTML::FormFu> supports any configuration file handled by
L<Config::Any>, most people tend to use YAML. First create a directory
to hold your form configuration files:
- mkdir -p root/forms/books
+ $ mkdir -p root/forms/books
-Then create the file C<root/forms/books/formfu_create.yml> and enter the
+Then create the file F<root/forms/books/formfu_create.yml> and enter the
following text:
---
# This is an optional 'mouse over' title pop-up
attributes:
title: Enter a book title here
-
+
# Another text field for the numeric rating
- type: Text
name: rating
label: Rating
attributes:
title: Enter a rating between 1 and 5 here
-
+
# Add a drop-down list for the author selection. Note that we will
# dynamically fill in all the authors from the controller but we
# could manually set items in the drop-list by adding this YAML code:
- type: Select
name: authors
label: Author
-
+
# The submit button
- type: Submit
name: submit
value: Submit
B<NOTE:> Copying and pasting YAML from Perl documentation is sometimes
-tricky. See the L<Config::General Config for this tutorial> section of
+tricky. See the L</Config::General Config for this tutorial> section of
this document for a more foolproof config format.
=head2 Update the CSS
-Edit C<root/static/css/main.css> and add the following lines to the
+Edit F<root/static/css/main.css> and add the following lines to the
bottom of the file:
...
=head2 Create a Template Page To Display The Form
-Open C<root/src/books/formfu_create.tt2> in your editor and enter the
+Open F<root/src/books/formfu_create.tt2> in your editor and enter the
following:
[% META title = 'Create/Update Book' %]
-
+
[%# Render the HTML::FormFu Form %]
[% form %]
-
- <p><a href="[% c.uri_for(c.controller.action_for('list'))
+
+ <p><a href="[% c.uri_for(c.controller.action_for('list'))
%]">Return to book list</a></p>
=head2 Add Links for Create and Update via C<HTML::FormFu>
-Open C<root/src/books/list.tt2> in your editor and add the following to
+Open F<root/src/books/list.tt2> in your editor and add the following to
the bottom of the existing file:
...
Rating: 4
Author: Comer
-Click the Submit button, and you will be returned to the Book List page
+Click the "Submit" button, and you will be returned to the Book List page
with a "Book created" status message displayed.
Also note that this implementation allows you to create books with any
=head2 Add Constraints
-Open C<root/forms/books/formfu_create.yml> in your editor and update it
+Open F<root/forms/books/formfu_create.yml> in your editor and update it
to match:
---
max: 40
# Override the default of 'Invalid input'
message: Length must be between 5 and 40 characters
-
+
# Another text field for the numeric rating
- type: Text
name: rating
min: 1
max: 5
message: "Must be between 1 and 5."
-
+
# Add a select list for the author selection. Note that we will
# dynamically fill in all the authors from the controller but we
# could manually set items in the select by adding this YAML code:
constraints:
# Make sure it's a number
- Integer
-
+
# The submit button
- type: Submit
name: submit
value: Submit
-
+
# Global filters and constraints.
constraints:
- # The user cannot leave any fields blank
- - Required
- # If not all fields are required, move the Required constraint to the
- # fields that are
+ # The user cannot leave any fields blank
+ - Required
+ # If not all fields are required, move the Required constraint to the
+ # fields that are
filter:
- # Remove whitespace at both ends
- - TrimEdges
- # Escape HTML characters for safety
- - HTMLEscape
+ # Remove whitespace at both ends
+ - TrimEdges
+ # Escape HTML characters for safety
+ - HTMLEscape
B<NOTE:> Copying and pasting YAML from Perl documentation is sometimes
tricky. See the L<Config::General Config for this tutorial> section of
The C<Select> element for C<authors> is changed from a single-select
drop-down to a multi-select list by adding configuration for the
-C<multiple> and C<size> options in C<formfu_create.yml>.
+C<multiple> and C<size> options in F<formfu_create.yml>.
=item *
spaces at the front or the back of the title and note that it will be
removed.
+Note that you can update your FormFu YAML forms and the development
+server does not need to reload -- the form definition is read from
+the YAML file each time a controller action uses it.
+
=head1 CREATE AND UPDATE/EDIT ACTION
Let's expand the work done above to add an edit action. First, open
-C<lib/MyApp/Controller/Books.pm> and add the following method to the
+F<lib/MyApp/Controller/Books.pm> and add the following method to the
bottom:
=head2 formfu_edit
-
+
Use HTML::FormFu to update an existing book
-
+
=cut
-
- sub formfu_edit :Chained('object') :PathPart('formfu_edit') :Args(0)
+
+ sub formfu_edit :Chained('object') :PathPart('formfu_edit') :Args(0)
:FormConfig('books/formfu_create.yml') {
my ($self, $c) = @_;
-
+
# Get the specified book already saved by the 'object' method
my $book = $c->stash->{object};
-
+
# Make sure we were able to get a book
unless ($book) {
- $c->flash->{error_msg} = "Invalid book -- Cannot edit";
- $c->response->redirect($c->uri_for($self->action_for('list')));
+ # Set an error message for the user & return to books list
+ $c->response->redirect($c->uri_for($self->action_for('list'),
+ {mid => $c->set_error_msg("Invalid book -- Cannot edit")}));
$c->detach;
}
-
+
# Get the form that the :FormConfig attribute saved in the stash
my $form = $c->stash->{form};
-
+
# Check if the form has been submitted (vs. displaying the initial
# form) and if the data passed validation. "submitted_and_valid"
# is shorthand for "$form->submitted && !$form->has_errors"
# Save the form data for the book
$form->model->update($book);
# Set a status message for the user
- $c->flash->{status_msg} = 'Book edited';
- # Return to the books list
- $c->response->redirect($c->uri_for($self->action_for('list')));
+ # Set a status message for the user & return to books list
+ $c->response->redirect($c->uri_for($self->action_for('list'),
+ {mid => $c->set_status_msg("Book edited")}));
$c->detach;
} else {
# Get the authors from the DB
# Populate the form with existing values from DB
$form->model->default_values($book);
}
-
+
# Set the template
$c->stash(template => 'books/formfu_create.tt2');
}
We have to manually specify the name of the FormFu .yml file as an
argument to C<:FormConfig> because the name can no longer be
automatically deduced from the name of our action/method (by default,
-FormFu would look for a file named C<books/formfu_edit.yml>).
+FormFu would look for a file named F<books/formfu_edit.yml>).
=item *
=item *
If the form has been submitted and passes validation, we skip creating a
-new book and just use C<$form-E<gt>model-E<gt>update> to update the
+new book and just use C<< $form->model->update >> to update the
existing book.
=item *
If the form is being displayed for the first time (or has failed
validation and it being redisplayed), we use
-C<$form-E<gt>model-E<gt>default_values> to populate the form with data
+C<< $form->model->default_values >> to populate the form with data
from the database.
=back
-Then, edit C<root/src/books/list.tt2> and add a new link below the
+Then, edit F<root/src/books/list.tt2> and add a new link below the
existing "Delete" link that allows us to edit/update each existing book.
-The last E<lt>tdE<gt> cell in the book list table should look like the
+The last <td> cell in the book list table should look like the
following:
...
<td>
[% # Add a link to delete a book %]
- <a href="[% c.uri_for(c.controller.action_for('delete'), [book.id]) %]">Delete</a>
+ <a href="[%
+ c.uri_for(c.controller.action_for('delete'), [book.id]) %]">Delete</a>
[% # Add a link to edit a book %]
- <a href="[% c.uri_for(c.controller.action_for('formfu_edit'), [book.id]) %]">Edit</a>
+ <a href="[%
+ c.uri_for(c.controller.action_for('formfu_edit'), [book.id]) %]">Edit</a>
</td>
...
-B<Note:> Only add two lines (the "Add a link to edit a book" comment and
+B<Note:> Only add three lines (the "Add a link to edit a book" comment and
the href for C<formfu_edit>). Make sure you add it below the existing
C<delete> link.
=head2 Config::General Config for this tutorial
If you are having difficulty with YAML config above, please save the
-below into the file C<formfu_create.conf> and delete the
-C<formfu_create.yml> file. The below is in L<Config::General> format
+below into the file F<formfu_create.conf> and delete the
+F<formfu_create.yml> file. The below is in L<Config::General> format
which follows the syntax of Apache config files.
constraints Required
Feel free to contact the author for any errors or suggestions, but the
best way to report issues is via the CPAN RT Bug system at
-<https://rt.cpan.org/Public/Dist/Display.html?Name=Catalyst-Manual>.
-
-The most recent version of the Catalyst Tutorial can be found at
-L<http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Manual/5.80/trunk/lib/Catalyst/Manual/Tutorial/>.
+L<https://rt.cpan.org/Public/Dist/Display.html?Name=Catalyst-Manual>.
-Copyright 2006-2010, Kennedy Clark, under the
+Copyright 2006-2011, Kennedy Clark, under the
Creative Commons Attribution Share-Alike License Version 3.0
(L<http://creativecommons.org/licenses/by-sa/3.0/us/>).