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:
...
</p>
This adds a new link to the bottom of the book list page that we can use
-to easily launch our HTML::FormFu-based form.
+to easily launch our L<HTML::FormFu>-based form.
=head2 Test The HTML::FormFu Create Form
$ script/myapp_server.pl -r
Login as C<test01> (password: mypass). Once at the Book List page,
-click the new HTML::FormFu "Create" link at the bottom to display the
+click the new L<HTML::FormFu> "Create" link at the bottom to display the
form. Fill in the following values:
Title: Internetworking with TCP/IP Vol. II
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
+tricky. See the L</Config::General Config for this tutorial> section of
this document for a more foolproof config format.
The main changes are:
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 *
Make sure you are still logged in as C<test01> and try adding a book
with various errors: title less than 5 characters, non-numeric rating, a
rating of 0 or 6, etc. Also try selecting one, two, and zero authors.
-When you click Submit, the HTML::FormFu C<constraint> items will
+When you click Submit, the L<HTML::FormFu> C<constraint> items will
validate the logic and insert feedback as appropriate. Try adding blank
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
- <elements>
- <constraints>
- min 5
- max 40
- type Length
- message Length must be between 5 and 40 characters
- </constraints>
- filter TrimEdges
- filter HTMLEscape
- name title
- type Text
- label Title
- <attributes>
- title Enter a book title here
- </attributes>
- </elements>
- <elements>
- constraints Integer
- filter TrimEdges
- filter NonNumeric
- name rating
- type Text
- label Rating
- <attributes>
- title Enter a rating between 1 and 5 here
- </attributes>
- </elements>
- <elements>
- constraints Integer
- filter TrimEdges
- filter HTMLEscape
- name authors
- type Select
- label Author
- multiple 1
- size 3
- </elements>
- <elements>
- value Submit
- name submit
- type Submit
- </elements>
- indicator submit
+ constraints Required
+ <elements>
+ <constraints>
+ min 5
+ max 40
+ type Length
+ message Length must be between 5 and 40 characters
+ </constraints>
+ filter TrimEdges
+ filter HTMLEscape
+ name title
+ type Text
+ label Title
+ <attributes>
+ title Enter a book title here
+ </attributes>
+ </elements>
+ <elements>
+ constraints Integer
+ filter TrimEdges
+ filter NonNumeric
+ name rating
+ type Text
+ label Rating
+ <attributes>
+ title Enter a rating between 1 and 5 here
+ </attributes>
+ </elements>
+ <elements>
+ constraints Integer
+ filter TrimEdges
+ filter HTMLEscape
+ name authors
+ type Select
+ label Author
+ multiple 1
+ size 3
+ </elements>
+ <elements>
+ value Submit
+ name submit
+ type Submit
+ </elements>
+ indicator submit
=head1 AUTHOR
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/>).
+(L<https://creativecommons.org/licenses/by-sa/3.0/us/>).