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's name() 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 its parent_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 from Rose::HTML::Object, which inherits from Rose::Object, which defines the init() method, which is called from the constructor. See the Rose::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.

coalesce_hidden_fields [BOOL]

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 the name() 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.

hidden_fields

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.

html_hidden_fields

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 on params(). That means that default values for fields should not be considered unless they are explicitly part of params().

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 is clear() the form. If a no_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 the input_value() for that field.

If a field "isa" Rose::HTML::Form::Field::Compound, and if no parameter exactly matches the name() of the compound field, then each subfields may be initialized by a parameter name that matches the subfield's name().

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 if params_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 field name(), if the object has a method with the same name, then the return value of that method is passed as the input_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 of Rose::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 the internal_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 of Rose::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 set error() 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. If uri_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 by self_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 of self_uri()). The default is "&".

validate

Validate the form by calling validate() on each field. If any field returns false from its validate() 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.

xhtml_hidden_fields

Returns the XHTML serialization of the fields returned by hidden_fields(), joined by newlines.

AUTHOR

John C. Siracusa (siracusa@mindspring.com)

COPYRIGHT

Copyright (c) 2004 by John C. Siracusa. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.