FAQ
It may be covered by your callback idea, but if I were using it I'm sure
I would want to arrange the form how I liked (or more correctly, someone
with a clue about interface would arrange it). Would something like this
be possible?

[% form.start %]
<table><tr><td>Your email address:</td><td>[% form.email
%]</td></tr></table>
[% form.stop %]

So that formmanager (gener/valid)ates the form elements, but the
structure can be changed?

John

Andrew Ford wrote:
I have been working away on a module called CGI::FormManager for a
couple of months now (I registered it with CPAN a month or so ago and
gave a talk on it at the Birminham perl mongers meeting at the end of
October). The module is intended to render forms from definitions of
the forms and validate submitted data against those definitions. It
is inspired in part by CGI:FormBuilder, but delegates validation to
Data::FormValidator. I intend to use it with Catalyst and Template
Toolkit, but of course its application is wider than that.

Given that the module works with Catalyst I am making a first
announcement of version 0.04 here. The source code is available from:

http://andrew-ford.com/perl/cgi-formmanager/

Bear in mind that this is alpha-stage code -- I may change any aspect
of it at the moment, although I do have a client using an even earlier
version on a production web site.

I've attached an introduction to the module below.

I would welcome comments on the design and on any other aspect of it.

Andrew



NAME
CGI::FormManager::Manual::Intro - introduction to the Form Manager

INTRODUCTION
Coding for CGI forms is tedious. Often an application that accepts
user
data will display an initial version of a form, check the user's data
then display another version of the form indicating errors in the
data;
then when the data passes all validation checks it may display a
confirmation page containing the user's data, allowing the user to
confirm, cancel or go back and edit the data.

That is a lot of code - a lot of very tedious code. What I would
prefer
to have is a definition of the structure of the form and have all the
code generated for me. This is what "CGI::FormManager" attempts to
do --
it manages forms, validating and rendering them. It is inspired by
"Data::FormValidator" and "CGI::FormBuilder".

The module renders form with initial data, with data submitted by the
user, and will also render a confirmation form. It uses an internal
default templates render form using tables (this may change to CSS at
some stage). By default forms are laid out with the labels in one
column
and the fields in the next column, but there are also facilities to
deal
with repeating groups of elements (such as a checkout form), and I am
thinking of providing the facility to specify groups of fields (for
example for a billing address and a shipping address laid out side by
side).

Validation is delegated to "Data::FormValidator", but the form
validation profile specified is augmented with information derived
from
the definition of the fields making up the form.

NOTE: this module is still experimental and in a state of flux. I am
still playing with the API and the manner of declaring forms, and am
trying to get it all to "feel right".

AN EXAMPLE
Our example application is a fairly typical user registration
application with an initial data entry page and a data confirmation
page. The application uses Template Toolkit for rendering and Catalyst
as the application framework.

The HTML Templates
The template for the initial data entry page would look like:

<h1>Sample App</h1>

<p>Some instructions here.</p>
<p>Please fill in this form:</p>

[% form %]

Our Template Toolkit setup will wrap the output of the template in
"<html>" and "<body>" elements, and insert a "<head>" element and all
the other paraphernalia to give a complete HTML document.

The "form" variable is set up in the application code, which we will
come to later. It is a "CGI::FormManager::Form" object and as such
renders itself as HTML in a string context.

If there are errors in the data submitted then we go to the
corrections
page:

<h1>Sample App</h1>

<p>There were some problems with the data you entered</p>
<p>[% message %]</p>

[% form %]

Of course this fragment and the previous one are so similar that one
might want to roll them together. [EXPAND]

When we are happy with the data we display the confirmation form:

<h1>Sample App</h1>

<p>Thank you for entering your details. Please check the data
you entered and click confirm, edit or cancel,</p>

[% form.render_confirmation %]

The Form Definition
The form is defined in a hash passed to the "CGI::FormManager"
package.
The hash contains three elements: "options", "elements" and
"validation".

This is what our form definition might look like:

my $form_defn = {
options => { method => 'POST',
action => $url,
},
elements => [ '<h2>Your login credentials</h2>',
cfm_text_field( name => 'email',
label => 'Email address',
size => 40,
maxlength => 50,
constraint => email()
REQUIRED ),
cfm_password_field( name => 'password',
REQUIRED),
cfm_password_field( name => 'password2',
label => 'Repeat your
password',
REQUIRED),

'<h2>Personal Details</h2>',
cfm_text_field( name => 'first_name',
REQUIRED ),
...

],
validation => { required => [ qw(email password) ],
constraint_methods => {
email => email(),
},
},
};

The "options" element is fairly self-explanatory, specifying the
URL for
the form and that the form should be submitted with a POST request.

The "elements" element lists the "elements" that make up the form.
"CGI::FormManager" exports a set of subroutines for creating form
elements of different types; any plain text strings are regarded as a
literal text to be included in the form. Fields are named and may
have a
label, which will be included in front of the field element in the
HTML
document. If no label is specified then a label is created from the
field name by upper-casing the first letter and replacing underscores
with spaces. Constraints may be specified in the field constructors or
may be specified in the "validation" element.

The "validation" element specifies how the form should be
validated. It
is augmented with information inferred from the field elements and
passed to "Data::FormValidator" when the form is validated.

The Application Code
The sample application is a Catalyst application, using a Template
Toolkit view component. See the Catalyst documentation for more
information.

The main application package instantiates a "CGI::FormManager"
object to
manage the forms (which are defined in the controller packages),
storing
it in the application's configuration and then sets up the Catalyst
application.

package MyRegApp;

use strict;
use Catalyst qw/-Debug/;

use CGI::FormManager;

MyRegApp->config( name => 'MyRegApp',
fmgr => CGI::FormManager->new() );
MyRegApp->setup;

sub end : Global {
my ($self, $c) = @_;
$c->stash->{template} ||= "default.tt2";
$c->forward('MyRegApp::View::TT') unless $c->res->output;
die if $c->req->params->{die};
}

1;

Registration is handled in a Catalyst controller package. It
defines the
form and loads it into the form manager (which it picks out of the
application's configuration) as the package is loaded. Handling
registration requests is done in the "user_details" action routine.

package MyRegApp::Controller::Registration;

use strict;
use base 'Catalyst::Base';
use CGI::FormManager qw(:element_decls :constraint_methods);

my $form_defn = ...; # (defined above)

MyRegApp->config->{'fmgr'}->add_form(user_details => $form_defn);

sub default : Private {
my($self, $c) = @_;
$c->forward('user_details');
}

sub user_details : Local {
my($self, $c) = @_;
my $form = $c->config->{fmgr}->form('user_details');
my $params = $c->request->params;
my $stash = $c->stash;

if (!keys %$params) {
$c->stash->{template} = "registration/new_user.tt2";
}
else {
$form = $form->validate($params);
if (!$form) {
$c->log->debug("validation failed" );
$c->log->debug(" missing:" . join(", ", $form->missing));
$c->log->debug(" invalid:" . join(", ", $form->invalid));
$c->stash->{template} =
"registration/new_user_corrections.tt2";
$c->stash->{message} = <<EOS;
There are problems with the information you supplied,
please correct these and resubmit.
EOS
}
else {
$c->log->debug("validation passed");
if ($form->confirmed_data) {
# store the new user in the database
$c->stash->{template} = "registration/welcome.tt2";
}
else {
$c->stash->{template} = "registration/new_user_confirm.tt2";
}
}
$c->stash->{form} = $form;
}
}

The action method fetches the form object.

If there were no parameters then it is a new request so it sets the
template to be used to be the new user form page. Otherwise it gets
the
form manager to validate the parameters supplied against the form
definition.

If validation fails then the corrections template is used,
otherwise we
look and see if it was a confirmation form that was submitted. If we
have come from the confirmation page then create the new user and
display a welcome page, otherwise display the confirmation page.
(Actually this routine doesn't included code for handling the user
clicking on "edit" or "cancel" on the confirmation page, or the actual
user creation, but you get the idea.)

The general form of an action method is:

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

# Set up the template we are going to use (may be updated later)
$c->stash->{template} = 'template-name';

# Validate the submitted data against the form
my $results = $fmgr->validate(form1 => $c->request);

# Make the form-results object visible to the template
$c->stash->{form} = $results;

if ($results) {
# The data is valid (NB evaluating $results in a boolean
# context is the same as $results->success)

if ($results->confirmed_data) {
# do something with the data, according to whether
# the user clicked "confirm", "edit" or "cancel"
# (will need change the template used)
}
else {
# data is OK, might want the user to confirm
$c->stash->{template} = 'form1-confirm-template';
}
}
elsif ($results->submitted) {
# Data was received but is not valid -- do nothing and the
# form will be redisplayed with appropriate error messages.
# Note though that if $results->confirmed_data is true then
# there may be a problem -- the confirmation form was
# submitted but the data stored in hidden fields might
# have been modified maliciously.
}
else {
# no data was received so just display the initial form
}
}

OTHER FEATURES
Lookup Elements
These elements allow text to be generated by a callback routine.

Repeating Groups of Fields
Applications such as shopping carts require the ability to display
repeating groups of form elements. This is the syntax I have come up
with:

cfm_repeating_group( elements =>
[ cfm_hidden_field ( name => 'prodcode' ),
cfm_lookup_element( name => 'desc',
header => 'Description',
value => \&_row_description ),
cfm_text_field ( name => 'qty',
header => 'Quantity'),
cfm_lookup_element( name => 'price',
value => \&_row_price ),
cfm_lookup_element( name => 'line_total',
value => \&_row_total,
footers => [ \&_sub_total,
\&_postage,
\&_tax,
\&_total ] )
],

This will be rendered as a table of four columns (the hidden field
will
be prepended to the contents of the first column). The number of rows
will be determined from the form data and there will be four footer
rows
for the calculated cells specified on the line total element.

OUTSTANDING ISSUES
This software should be considered alpha quality. It has not been
extensively tested and the exact details of the API have not been
finalized. However there are applications using it that are in
production use!

This is a partial list of outstanding issues.

* expand the documentation

* complete the initialization of form field objects (defaults, cross
population of validation/elements, radio field groups, etc)

* review the use of CSS classes on elements

* automatically generate JavaScript validation code (as
"CGI::FormBuilder" does)

* add objects to group elements -- for example for side-by-side
layout
of billing and shipping addresses.

* expand the test suite

* design a proper Catalyst plugin

* have textual representation of form specification that can be read

SEE ALSO
Data::FormValidator, Template, CGI::FormBuilder

AUTHOR
Andrew Ford <A.Ford@ford-mason.co.uk>

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



_______________________________________________
Catalyst mailing list
Catalyst@lists.rawmode.org
http://lists.rawmode.org/mailman/listinfo/catalyst

Search Discussions

Discussion Posts

Previous

Follow ups

Related Discussions

Discussion Navigation
viewthread | post
posts ‹ prev | 5 of 8 | next ›
Discussion Overview
groupcatalyst @
categoriescatalyst, perl
postedNov 22, '05 at 1:12p
activeNov 22, '05 at 8:06p
posts8
users6
websitecatalystframework.org
irc#catalyst

People

Translate

site design / logo © 2022 Grokbase