NAME
Rose::HTML::Form - HTML form base class.
SYNOPSIS
package RegistrationForm;
use Rose::HTML::Form;
our @ISA = qw(Rose::HTML::Form);
use Person;
use Rose::HTML::Form::Field::Text;
use Rose::HTML::Form::Field::Email;
use Rose::HTML::Form::Field::PhoneNumber::US;
sub build_form
{
my($self) = shift;
my %fields;
$fields{'name'} =
Rose::HTML::Form::Field::Text->new(name => 'name',
size => 25);
$fields{'email'} =
Rose::HTML::Form::Field::Email->new(name => 'email',
size => 50);
$fields{'phone'} =
Rose::HTML::Form::Field::PhoneNumber::US->new(name => 'phone');
...
$self->add_fields(%fields);
}
sub validate
{
my($self) = shift;
my $ok = $self->SUPER::validate(@_);
return $ok unless($ok);
if($self->field('name')->internal_value =~ /foo/ &&
$self->field('phone')->internal_value =~ /123/)
{
$self->error('...');
return 0;
}
...
return 1;
}
sub init_with_person
{
my($self, $person) = @_;
$self->init_with_object($person);
$self->field('phone2')->input_value($person->alt_phone);
$self->field('is_new')->input_value(1);
...
}
sub person_from_form
{
my($self) = shift;
my $person = $self->object_from_form(class => 'Person');
$person->alt_phone($self->field('phone2')->internal_value);
...
return $person;
}
...
my $form = RegistrationForm->new;
if(...)
{
my $params = MyWebServer->get_query_params();
$form->params($params);
$form->init_fields();
unless($form->validate)
{
return error_page(error => $form->error);
}
$person = $form->person_from_form();
do_something_with($person);
...
}
else
{
$person = get_person(...);
$form->init_with_person($person);
display_page(form => $form);
}
...
DESCRIPTION
Rose::HTML::Form
is more than just an object representation of the <form> HTML tag. It is meant to be a base class for custom form classes that can be initialized with and return "rich" values such as objects, or collections of objects.
Building up a reusable library of form classes is extremely helpful when building large web applications with forms that may appear in many different places. Similar forms can inherit from a common subclass.
This class inherits from, and follows the conventions of, Rose::HTML::Object
. Inherited methods that are not overridden will not be documented a second time here. See the Rose::HTML::Object
documentation for more information.
OVERVIEW
Rose::HTML::Form
objects are meant to encapsulate an entire HTML form, including all fields within the form. While individual fields may be queried and manipulated, the intended purpose of this class is to treat the form as a "black box" as much as possible.
For example, instead of asking a form object for the values of the "name", "email", and "phone" fields, the user would ask the form object to return a new "Person" object that encapsulates those values.
Form objects should also accept initialization through the same kinds of objects that they return. Subclasses are encouraged to create methods such as (to use the example described above) init_with_person()
and person_from_form()
in order to do this. The generic methods init_with_object()
and object_from_form()
are meant to ease the implementation of such custom methods.
Form objects can also take input through a hash. Each hash key correspond to a field (or subfield) name, and each value is either a scalar or a reference to an array of scalars (for multiple-value fields). This hash of parameters can be queried and manipulated before finally calling init_fields()
in order to initialize the fields based on the current state of the parameters.
Compound fields (fields consisting of more than one HTML field, such as a month/day/year date field with separate text fields for each element of the date) may be "addressed" by hash arguments using both top-level names (e.g., "birthday") or by subfield names (e.g., "birthday.month", "birthday.day", "birthday.year"). If the top-level name exists in the hash, then subfield names are ignored.
(See Rose::HTML::Form::Field::Compound
for more information on compound fields.)
Each form has a list of field objects. Each field object is stored under a name, which may or may not be the same as the field name, which may or may not be the same as the "name" HTML attribute for any of the HTML tags that make up that field.
Forms are validated by calling validate()
on each field object. If any individual field does not validate, then the form is invalid. Inter-field validation is the responsibility of the form object.
HTML ATTRIBUTES
Valid attributes:
accept
accept-charset
accesskey
action
class
dir
enctype
id
lang
method
name
onblur
onclick
ondblclick
onfocus
onkeydown
onkeypress
onkeyup
onmousedown
onmousemove
onmouseout
onmouseover
onmouseup
onreset
onsubmit
style
tabindex
title
value
xml:lang
Required attributes (default values in parentheses):
action
enctype (application/x-www-form-urlencoded)
method (get)
CONSTRUCTOR
- new PARAMS
-
Constructs a new
Rose::HTML::Form
object based on PARAMS, where PARAMS are name/value pairs. Any object method is a valid parameter name.
OBJECT METHODS
- add_field ARGS
-
Convenience alias for
add_fields()
. - add_fields ARGS
-
Add the fields specified by ARGS to the list of fields contained in this form.
If an argument is "isa"
Rose::HTML::Form::Field
, then it is added to the list of fields, stored under the name returned by the field'sname()
method.If an argument is anything else, it is used as the field name, and the next argument is used as the field object to store under that name. If the next argument is not an object derived from
Rose::HTML::Form::Field
, then a fatal error occurs.The field object's
name()
is set to the name that it is stored under, and itsparent_field()
is set to the form object.Returns the full list of field objects, sorted by field name, in list context, or a reference to a list of the same in scalar context.
Examples:
$name_field = Rose::HTML::Form::Field::Text->new(name => 'name', size => 25); $email_field = Rose::HTML::Form::Field::Text->new(name => 'email', size => 50); # Field arguments $form1->add_fields($name_field, $email_field); # Name/field pairs $form2->add_fields(name => $name_field, email => $email_field); # Mixed $form3->add_fields($name_field, email => $email_field);
- add_param_value NAME, VALUE
-
Add VALUE to the parameter named NAME. Example:
$form->param(a => 1); print $form->param('a'); # 1 $form->add_param_value(a => 2); print join(',', $form->param('a')); # 1,2
- build_form
-
This method is a no-op in this class. It is meant to be overridden by subclasses. It is called at the end of the
init()
method. (Remember that this class inherits fromRose::HTML::Object
, which inherits fromRose::Object
, which defines theinit()
method, which is called from the constructor. See theRose::Object
documentation for more information.)Subclasses should populate the field list in their overridden versions of
build_field()
. Example:sub build_form { my($self) = shift; my %fields; $fields{'name'} = Rose::HTML::Form::Field::Text->new(name => 'name', size => 25); $fields{'email'} = Rose::HTML::Form::Field::Email->new(name => 'email', size => 50); $fields{'phone'} = Rose::HTML::Form::Field::PhoneNumber::US->new(name => 'phone'); ... $self->add_fields(%fields); }
- clear_fields
-
Call
clear()
on each field object. -
Get or set the boolean flag that controls how compound field values are encoded in hidden fields. If this flag is true, then each compound field is encoded as a single hidden field. If the flag is false (the default), then each subfield of a compound field will have its own hidden field.
- coalesce_query_string_params [BOOL]
-
Get or set the boolean flag that controls how compound field values are encoded in the query string. If this flag is true (the default), then compound fields are represented by a single query parameter. Otherwise, the subfields of each compound field appear as separate query parameters.
- delete_field NAME
-
Delete the field stored under the name NAME. If NAME "isa"
Rose::HTML::Form::Field
, then thename()
method is called on it and the return value is used as NAME. - delete_fields
-
Delete all fields, leaving the list of fields empty.
- delete_param NAME [, VALUES]
-
If just the NAME argument is passed, the parameter named NAME is deleted.
If VALUES are also passed, then VALUES are deleted from the set of values held by the parameter name NAME. If only one value remains, then it is the new value for the NAME parameter (i.e., the value is no longer an array reference, but a scalar instead). If every value is deleted, then the NAME parameter is deleted as well. Example:
$form->param(a => [ 1, 2, 3, 4 ]); $form->delete_param(a => 1); $vals = $form->param('a'); # [ 2, 3, 4 ] $form->delete_param(a => [ 2, 3 ]); $vals = $form->param('a'); # 4 $form->delete_param(a => 4); $vals = $form->param('a'); # undef $form->param_exists('a'); # false
- delete_params
-
Delete all parameters.
- end_html
-
Returns the HTML required to end the form.
- end_xhtml
-
Returns the XHTML required to end the form.
- end_multipart_html
-
Returns the HTML required to end a multipart form.
- end_multipart_xhtml
-
Returns the XHTML required to end a multipart form.
- field NAME [, VALUE]
-
Get or set the field specified by NAME. If only a NAME argument is passed, then the field stored under the name NAME is returned. If no field exists under that name exists, then undef is returned.
If both NAME and VALUE arguments are passed, then the field VALUE is stored under the name NAME. If VALUE is not an object derived from
Rose::HTML::Form::Field
, then a fatal error occurs. - fields
-
Returns the full list of field objects, sorted by field name, in list context, or a reference to a list of the same in scalar context.
- field_names
-
Returns a sorted list of field names in list context, or a reference to a list of the same in scalar context.
-
Returns one or more
Rose::HTML::Form::Field::Hidden
objects that represent the hidden fields needed to encode all of the field values in this form.If
coalesce_hidden_fields()
is true, then each compound field is encoded as a single hidden field. Otherwise, each subfield of a compound field will be have its own hidden field. -
Returns the HTML serialization of the fields returned by
hidden_fields()
, joined by newlines. - init_fields [ARGS]
-
Initialize the fields based on
params()
. In general, this works as you'd expect, but the details are a bit complicated.The intention of
init_fields()
is to set field values based solely and entirely onparams()
. That means that default values for fields should not be considered unless they are explicitly part ofparams()
.In general, default values for fields exist for the purpose of displaying the HTML form with certain items pre-selected or filled in. In a typical usage scenario, those default values will end up in the web browser form submission and, eventually, as as an explicit part of part
params()
, so they are not really ignored.But to preserve the intended functionality of
init_fields()
, the first thing this method does isclear()
the form. If ano_clear
parameter with a true value is passed as part of ARGS, then this step is skipped.If a parameter name exactly matches a field's name (note: the field's
name()
, not the name that the field is stored under in the form, which may be different), then the (list context) value of that parameter is passed as theinput_value()
for that field.If a field "isa"
Rose::HTML::Form::Field::Compound
, and if no parameter exactly matches thename()
of the compound field, then each subfields may be initialized by a parameter name that matches the subfield'sname()
.If a field is an "on/off" type of field (e.g., a radio button or checkbox), then the field is turned "on" only if the value of the parameter that matches the field's
name()
exactly matches (string comparison) the "value" HTML attribute of the field. If not, and ifparams_exist()
, then the field is set to "off". Otherwise, the field is not modified at all.Examples:
package RegistrationForm; ... sub build_form { my($self) = shift; my %fields; $fields{'name'} = Rose::HTML::Form::Field::Text->new( name => 'your_name', size => 25); $fields{'gender'} = Rose::HTML::Form::Field::RadioButtonGroup->new( name => 'gender', radio_buttons => { 'm' => 'Male', 'f' => 'Female' }, default => 'm'); $fields{'hobbies'} = Rose::HTML::Form::Field::CheckBoxGroup->new( name => 'hobbies', checkboxes => [ 'Chess', 'Checkers', 'Knitting' ], default => 'Chess'); $fields{'bday'} = Rose::HTML::Form::Field::DateTime::Split::MonthDayYear->new( name => 'bday'); $self->add_fields(%fields); # Set a different "name" HTML attribute for this field. # Has to be done after the call to add_fields() because # add_fields() sets the name() of each field to match the # name that it is stored under. $self->field('name')->html_attr(name => 'your_name'); } ... my $form = RegistrationForm->new(); $form->params(name => 'John', gender => 'm', hobbies => undef, bday => '1/24/1984'); # John, Male, no hobbies, 1/24/1984 $form->init_fields; $form->reset; $form->params(name => 'John', bday => '1/24/1984'); # No name, Male, Chess, 1/24/1984 $form->init_fields(no_clear => 1); $form->reset; # Set using subfield names for "bday" compound field $form->params('your_name' => 'John', 'bday.month' => 1, 'bday.day' => 24, 'bday.year' => 1984); # John, Male, no hobbies, 1/24/1984 $form->init_fields(); $form->reset; $form->params('bday' => '1/24/1984', 'bday.month' => 12, 'bday.day' => 25, 'bday.year' => 1975); # No name, no gender, no hobbies, but 1/24/1984 because # the "bday" param trumps any and all subfield params. $form->init_fields(); $form->reset; # Explicitly set hobbies field to Knitting... $form->field('hobbies')->input_value('Knitting'); # ...but then provide a hobbies param with no value $form->params('hobbies' => undef); # Fields are not cleared, but the existence of the hobbies # param with an empty value causes the hobbies list to be # empty, instead of the default Chess. Thus: # No name, Male, no hobbies, no birthday $form->init_fields(no_clear => 1);
- init_with_object OBJECT
-
Initialize the form based on OBJECT. First, the form is
clear()
ed. Next, for each fieldname()
, if the object has a method with the same name, then the return value of that method is passed as theinput_value()
for the form field of the same name.Heck, at this point, the actual code for the
init_with_object()
method is shorter and more clear than my description. Basically, it does this:sub init_with_object { my($self, $object) = @_; $self->clear(); foreach my $field ($self->fields) { my $name = $field->name; if($object->can($name)) { $field->input_value($object->$name()); } } }
Use this method as a "helper" when writing your own methods such as
init_with_person()
, as described in the example in the OVERVIEW.init_with_object()
should be called in the code for subclasses ofRose::HTML::Form
, but never by an end-user of such classes.The convention for naming such methods is "init_with_foo", where "foo" is a (lowercase, underscore-separated, please) description of the object (or objects) used to initialize the form. You are free to accept and handle any kind or number of arguments in your "init_with_foo()"-style methods (all which you'll carefully document, of course).
The field names may not match up exactly with the object method names. In such cases, you can use
init_with_object()
to handle all the fields that do match up with method names, and then handle the others manually. Example:sub init_with_person { my($self, $person) = @_; # Handle field names that match method names $self->init_with_object($person); # Manually set the non-matching or other fields $self->field('phone2')->input_value($person->alt_phone); $self->field('is_new')->input_value(1); ... }
- object_from_form OBJECT | CLASS | PARAMS
-
Returns an object built based on the contents of the form.
For each field
name()
, if the object has a method with the same name, then theinternal_value()
of the field is passed to the object method of that name. The actual code is just about as concise as my description:foreach my $field ($self->fields) { my $name = $field->name; if($object->can($name)) { $object->$name($field->internal_value); } }
To do this, the method needs an object. If passed an OBJECT argument, then that's the object that's used. If passed a CLASS name, then a new object is constructed by calling
new()
on that class. OBJECT or CLASS may alternately be passed as a name/value pair in PARAMS.Use this method as a "helper" when writing your own methods such as
person_from_form()
, as described in the example in the OVERVIEW.object_from_form()
should be called in the code for subclasses ofRose::HTML::Form
, but never by an end-user of such classes.The convention for naming such methods is "foo_from_form", where "foo" is a (lowercase, underscore-separated, please) description of the object constructed based on the values in the form's fields.
The field names may not match up exactly with the object method names. In such cases, you can use
object_from_form()
to handle all the fields that do match up with method names, and then handle the others manually. Example:sub person_from_form { my($self) = shift; my $person = $self->object_from_form(class => 'Person'); $person->alt_phone($self->field('phone2')->internal_value); ... return $person; }
It is the caller's responsibility to ensure that the object class (
Person
in the example above) is loaded prior to calling this method. - param NAME [, VALUE]
-
Get or set the value of a named parameter. If just NAME is passed, then the value of the parameter of that name is returned. If VALUE is also passed, then the parameter value is set and then returned.
If a parameter has multiple values, the values are returned as a reference to an array in scalar context, or as a list in list context. Multiple values are set by passing a VALUE that is a reference to an array of scalars.
Failure to pass at least a NAME argument results in a fatal error.
- params [PARAMS]
-
Get or set all parameters at once.
PARAMS can be a reference to a hash or a list of name/value pairs. If a parameter has multiple values, those values should be provided in the form of a references to an array of scalar values. If the list of name/value pairs has an odd number of items, a fatal error occurs.
Regardless of the arguments, this method returns the complete set of parameters in the form of a hash (in list context) or a reference to a hash (in scalar context).
In scalar context, the hash reference returned is a reference to the actual hash used to store parameter names and values in the object. It should be treated as read-only.
The hash returned in list context is a shallow copy of the actual hash used to store parameter names and values in the object. It should also be treated as read-only.
If you want a read/write copy, make a deep copy of the hash reference return value and then modify the copy.
- params_exist
-
Returns true if any parameters exist, false otherwise.
- param_exists NAME
-
Returns true if a parameter named NAME exists, false otherwise.
- param_value_exists NAME, VALUE
-
Determines if a parameter of a particular name exists and has a particular value. This method returns true if the parameter named NAME exists and also has a value that is equal to (string comparison) VALUE. Otherwise, it returns false.
A fatal error occurs unless both NAME and VALUE arguments are passed.
- query_string
-
Returns a URI-escaped (but not HTML-escaped) query string that corresponds to the current state of the form. If
coalesce_query_string_params()
is true (which is the default), then compound fields are represented by a single query parameter. Otherwise, the subfields of each compound field appear as separate query parameters. - reset
-
Call
reset()
on each field object and seterror()
to undef. - reset_fields
-
Call
reset()
on each field object. - self_uri
-
Returns a
Rose::URI
object corresponding to the current state of the form. Ifuri_base()
is set, then it is included in front of what would otherwise be the start of the URI (i.e., the value of the form's "action" HTML attribute). - start_html
-
Returns the HTML that will begin the form tag.
- start_xhtml
-
Returns the XHTML that will begin the form tag.
- start_multipart_html
-
Sets the "enctype" HTML attribute to "multipart/form-data", then returns the HTML that will begin the form tag.
- start_multipart_xhtml
-
Sets the "enctype" HTML attribute to "multipart/form-data", then returns the XHTML that will begin the form tag.
- uri_base [STRING]
-
Get or set the URI of the form, minus the value of the "action" HTML attribute. Although the form action can be a relative URI, I suggest that it be an absolute path at the very least, leaving the
uri_base()
to be the initial part of the full URI returned byself_uri()
. Example:$form->action('/foo/bar'); $form->uri_base('http://www.foo.com'); # http://www.foo.com/foo/bar $uri = $form->self_uri;
- uri_separator [CHAR]
-
Get or set the character used to separate parameter name/value pairs in the return value of
query_string()
(which is in turn used to construct the return value ofself_uri()
). The default is "&". - validate
-
Validate the form by calling
validate()
on each field. If any field returns false from itsvalidate()
call, then this method returns false. Otherwise, it returns true. - validate_field_html_attrs [BOOL]
-
Get or set a boolean flag that indicates whether or not the fields of this form will validate their HTML attributes. If a BOOL argument is passed, then it is passed as the argument to a call to
validate_html_attrs()
on each field. In either case, the current value of this flag is returned. -
Returns the XHTML serialization of the fields returned by
hidden_fields()
, joined by newlines.
AUTHOR
John C. Siracusa (siracusa@mindspring.com)