NAME

Valiant::HTML::Formbuilder - General HTML Forms

SYNOPSIS

Given a model with the correct API such as:

package Local::Person;

use Moo;
use Valiant::Validations;

has first_name => (is=>'ro');
has last_name => (is=>'ro');

validates ['first_name', 'last_name'] => (
  length => {
    maximum => 10,
    minimum => 3,
  }
);

Wrap a formbuilder object around it and generate HTML form field controls:

my $person = Local::Person->new(first_name=>'J', last_name=>'Napiorkowski');
$person->validate;

my $fb = Valiant::HTML::FormBuilder->new(
  model => $person,
  name => 'person'
);

print $fb->input('first_name');
# <input id="person_first_name" name="person.first_name" type="text" value="J"/> 

print $fb->errors_for('first_name');
# <div>First Name is too short (minimum is 3 characters)</div> 

Although you can create a formbuilder instance directly as in the above example you might find it easier to use the export helper method "form_for" in Valiant::HTML::Form which encapsulates the display logic needed for creating the form tags. This builder creates form tag elements but not the actual form open and close tags.

DESCRIPTION

This class wraps an underlying data model and makes it easy to build HTML form elements based on the state of that model. Inspiration for this design come from Ruby on Rails Formbuilder as well as similar designs in the Phoenix Framework.

You can subclass this to future customize how your form elements display as well as to add more complex form elements for your templates.

Documentation here is basically API level, a more detailed tutorial will follow eventually but for now you'll need to review the source, test cases and example application bundled with this distribution for for hand holding.

Currently this is designed to work mostly with the Valiant model validation framework as well as the glue for DBIx:Class, DBIx:Class::Valiant, although I did take pains to try and make the API agnostic many of the test cases are assuming that stack and getting that integration working well is the primary use case for me. Thoughts and code to make this more stand alone are very welcomed.

ATTRIBUTES

This class defines the following attributes used in creating an instance.

model

This is the data model that the formbuilder inspects for field state and error conditions. This should be a model that does the API described here: "'REQUIRED MODEL API'" in Valiant::HTML::Form. Required but the API is pretty flexible (see docs).

Please note that my initial use case for this is using Valiant for validation and DBIx::Class as the model (via DBIx:Class::Valiant) so that combination has the most testing and examples. If you are using a different storage or validation setup you need to complete the API described. Please send test cases and pull requests to improve interoperability!

name

This is a string which is the internal name given to the model. This is used to set a namespace for form field name attributes and the default namespace for id attributes. Required.

options

A optional hashref of options used in form field generation. Some of these might become attributes in the future. Here's a list of the current options

index

The index of the formbuilder when it is a sub formbuilder with a parent and we are iterating over a collection.

child_index

When creating a sub formbuilder that is an element in a collection, this is used to pass the index value

builder

The package name of the current builder

parent_builder

The parent formbuilder instance to a sub builder.

include_id

Used to indicated that a sub formbuilder should add hidden fields indicating the storage ID for the current model.

namespace

The ID namespace; used to populate the namespace attribute.

as

Used to override how the class and ids are made for your forms.

index

The current index of a collection for which the current formbuilder is one item in.

namespace

Used to add a prefix to the ID for your form elements.

view

Optional. The view or template object that is using the formbuilder. If available can be used to influence how the HTML for controls are created.

METHODS

This class defines the following public instance methods.

model_errors

$fb->model_errors();
$fb->model_errors(\%attrs);
$fb->model_errors(\%attrs, \&template); # %attrs limited to 'max_errors' and 'show_message_on_field_errors'
$fb->model_errors(\&template);

Display model level errors, either with a default or custom template. 'Model' errors are errors that are not associated with a model attribute in particular, but rather the model as a whole.

Arguments to this method are optional. "\%attrs" is a hashref which is passed to the tag builder to create any needed HTML attributes (such as class and style). "\&template" is a coderef that gets the @errors as an argument and you can use it to customize how the errors are displayed. Otherwise we use a default template that lists the errors with an HTML ordered list, or a div if there's only one error.

"\%attrs" can also contain two options that gives you some additional control over the display

max_errors

Don't display more than a certain number of errors

show_message_on_field_errors

Sometimes you want a global message displayed when there are field errors. Valiant doesn't add a model error if there's field errors (although it would be easy for you to add this yourself with a model validation) so this makes it easy to display such a message. If a string or translation tag then show that, if its a '1' the show the default message, which is "Form has errors" unless you overide it.

This can be a useful option when you have a long form and you want a user to know there's errors possibly off the browser screen.

Examples. Assume two model level errors "Trouble 1" and "Trouble 2":

$fb->model_errors;
# <ol><li>Trouble 1</li><li>Trouble 2</li></ol>

$fb->model_errors({class=>'foo'});
# <ol class="foo"><li>Trouble 1</li><li>Trouble 2</li></ol>

$fb->model_errors({max_errors=>1});
# <div>Trouble 1</div>

$fb->model_errors({max_errors=>1, class=>'foo'})
# <div class="foo">Trouble 1</div>

$fb->model_errors({show_message_on_field_errors=>1})
# <ol><li>Form has errors</li><li>Trouble 1</li><li>Trouble 2</li></ol>

$fb->model_errors({show_message_on_field_errors=>"Bad!"})
# <ol><li>Bad!</li><li>Trouble 1</li><li>Trouble 2</li></ol>

$fb->model_errors(sub {
  my (@errors) = @_;
  join " | ", @errors;
});
# Trouble 1 | Trouble 2

label

$fb->label($attribute)
$fb->label($attribute, \%options)
$fb->label($attribute, $content)
$fb->label($attribute, \%options, $content) 
$fb->label($attribute, \&content);   sub content { my ($translated_attribute) = @_;  ... }
$fb->label($attribute, \%options, \&content);   sub content { my ( $translated_attribute) = @_;  ... }

Creates a HTML form element label with the given "\%options" passed to the tag builder to create HTML attributes and an optional "$content". If "$content" is not provided we use the human, translated (if available) version of the "$attribute" for the label content. Alternatively you can provide a template which is a subroutine reference which recieves the translated attribute as an argument. Examples:

$fb->label('first_name');
# <label for="person_first_name">First Name</label>

$fb->label('first_name', {class=>'foo'});
# <label class="foo" for="person_first_name">First Name</label>

$fb->label('first_name', 'Your First Name');
# <label for="person_first_name">Your First Name</label>

$fb->label('first_name', {class=>'foo'}, 'Your First Name');
# <label class="foo" for="person_first_name">Your First Name</label>

$fb->label('first_name', sub {
  my $translated_attribute = shift;
  return "$translated_attribute ",
    $fb->input('first_name');
});
# <label for="person_first_name">
#   First Name 
#   <input id="person_first_name" name="person.first_name" type="text" value="John"/>
# </label>

$fb->label('first_name', +{class=>'foo'}, sub {
  my $translated_attribute = shift;
  return "$translated_attribute ",
    $fb->input('first_name');
});
# <label class="foo" for="person_first_name">
#   First Name
#   <input id="person_first_name" name="person.first_name" type="text" value="John"/>
# </label>

errors_for

$fb->errors_for($attribute)
$fb->errors_for($attribute, \%options)
$fb->errors_for($attribute, \%options, \&template)
$fb->errors_for($attribute, \&template)

Similar to "model_errors" but for errors associated with an attribute of a model. Accepts the $attribute name, a hashref of \%options (used to set options controling the display of errors as well as used by the tag builder to create HTML attributes for the containing tag) and lastly an optional \&template which is a subroutine reference that received an array of the translated errors for when you need very custom error display. If omitted we use a default template displaying errors in an ordered list (if more than one) or wrapped in a div tag (if only one error).

\%options used for error display and which are not passed to the tag builder as HTML attributes:

max_errors

Don't display more than a certain number of errors

Assume the attribute 'last_name' has the following two errors in the given examples: "first Name is too short", "First Name contains non alphabetic characters".

$fb->errors_for('first_name');
# <ol><li>First Name is too short (minimum is 3 characters)</li><li>First Name contains non alphabetic characters</li></ol>

$fb->errors_for('first_name', {class=>'foo'});
# <ol class="foo"><li>First Name is too short (minimum is 3 characters)</li><li>First Name contains non alphabetic characters</li></ol>

$fb->errors_for('first_name', {class=>'foo', max_errors=>1});
# <div class="foo">First Name is too short (minimum is 3 characters)</div>

$fb->errors_for('first_name', sub {
  my (@errors) = @_;
  join " | ", @errors;
});
# First Name is too short (minimum is 3 characters) | First Name contains non alphabetic characters

input

$fb->input($attribute, \%options)
$fb->input($attribute)

Create an input form tag using the $attribute's value (if any) and optionally passing a hashref of \%options which are passed to the tag builder to create HTML attributes for the input tag. Optionally add errors_classes which is a string that is appended to the class attribute when the $attribute has errors. Examples:

$fb->input('first_name');
# <input id="person_first_name" name="person.first_name" type="text" value="J"/>

$fb->input('first_name', {class=>'foo'});
# <input class="foo" id="person_first_name" name="person.first_name" type="text" value="J"/>

$fb->input('first_name', {errors_classes=>'error'});
# <input class="error" id="person_first_name" name="person.first_name" type="text" value="J"/>

$fb->input('first_name', {class=>'foo', errors_classes=>'error'});
# <input class="foo error" id="person_first_name" name="person.first_name" type="text" value="J"/>

Special \%options:

errors_classes

A string that is appended to the class attribute if the $attribute has errors (as defined by the model API)

password

$fb->password($attribute, \%options)
$fb->password($attribute)

Create a password HTML form field. Similar to "input" but sets the type to 'password' and also sets value to '' since generally you don't want to show the current password (and if you are doing the right thing and saving a 1 way hash not the plain text you don't even have it to show anyway).

Example:

$fb->password('password');
# <input id="person_password" name="person.password" type="password" value=""/>

$fb->password('password', {class='foo'});
# <input class="foo" id="person_password" name="person.password" type="password" value=""/>

$fb->password('password', {class='foo', errors_classes=>'error'});
# <input class="foo error" id="person_password" name="person.password" type="password" value=""/>

hidden

$fb->hidden($attribute, \%options)
$fb->hidden($attribute)

Create a hidden HTML form field. Similar to "input" but sets the type to 'hidden'.

$fb->hidden('id');
# <input id="person_id name="person.id" type="hidden" value="101"/>

$fb->hidden('id', {class='foo'});
# <input class="foo" id="person_id name="person.id" type="hidden" value="101"/>

text_area

$fb->text_area($attribute);
$fb->text_area($attribute, \%options);

Create an HTML text_area tag based on the attribute value and with optional \%options which is a a hashref passed to the tag builder for generating HTML attributes. Can also set errors_classes that will append a string of additional CSS classes when the $attribute has errors. Examples:

$fb->text_area('comments');
# <textarea id="person_comments" name="person.comments">J</textarea>

$fb->text_area('comments', {class=>'foo'});
# <textarea class="foo" id="person_comments" name="person.comments">J</textarea>

$fb->text_area('comments', {class=>'foo', errors_classes=>'error'});
# <textarea class="foo error" id="person_comments" name="person.comments">J</textarea>

Special \%options:

errors_classes

A string that is appended to the class attribute if the $attribute has errors (as defined by the model API)

checkbox

$fb->checkbox($attribute);
$fb->checkbox($attribute, \%options);
$fb->checkbox($attribute, $checked_value, $unchecked_value);
$fb->checkbox($attribute, \%options, $checked_value, $unchecked_value);

Generate an HTML form checkbox element with its state based on evaluating the value of $attribute in a boolean context. If $attribute is true then the checkbox will be checked. May also pass a hashref of \%options, which contain render instructions and HTML attributes used by the tag builder. "$checked_value" and "$unchecked_value" specify the values when the checkbox is checked or not (defaults to 1 for checked and 0 for unchecked, but $unchecked is ignored if option include_hidden is set to false; see below).

Special \%options:

errors_classes

A string that is appended to the class attribute if the $attribute has errors (as defined by the model API)

include_hidden

Defaults to true. Since the rules for an HTML form checkbox specify that if the checkbox is 'unchecked' then nothing is submitted. This can cause issues if you are expecting a submission that somehow indicates 'unchecked' For example you might have a status field boolean where unchecked should indicate 'false'. So by default we add a hidden field with the same name as the checkbox, with a value set to $unchecked_value (defaults to 0). In the case where the field is checked then you'll get two values for the same field name so you should have logic that in the case that field name is an array then take the last one (if you are using Plack::Request this is using Hash::MultiValue which does this by default; if you are using Catalyst you can use the use_hash_multivalue_in_request option or you can use something like Catalyst::TraitFor::Request::StructuredParameters which has options to help with this. If you are using Mojolicious then the param method works this way as well.

checked

A boolean value to indicate if the checkbox field is 'checked' when generated. By default a checkbox state is determined by the value of $attribute for the underlying model. You can use this to override (for example you might wish a checkbox default state to be checked when creating a new entry when it would otherwise be false).

Examples:

$fb->checkbox('status');
# <input name="person.status" type="hidden" value="0"/>
# <input id="person_status" name="person.status" type="checkbox" value="1"/>

$fb->checkbox('status', {class=>'foo'});
# <input name="person.status" type="hidden" value="0"/>
# <input class="foo" id="person_status" name="person.status" type="checkbox" value="1"/>

$fb->checkbox('status', 'active', 'deactive');
# <input name="person.status" type="hidden" value="deactive"/>
# <input id="person_status" name="person.status" type="checkbox" value="active"/>

$fb->checkbox('status', {include_hidden=>0});
# <input id="person_status" name="person.status" type="checkbox" value="1"/>

$person->status(1);
$fb->checkbox('status', {include_hidden=>0});
# <input checked id="person_status" name="person.status" type="checkbox" value="1"/>

$person->status(0);
$fb->checkbox('status', {include_hidden=>0, checked=>1});
# <input checked id="person_status" name="person.status" type="checkbox" value="1"/>

$fb->checkbox('status', {include_hidden=>0, errors_classes=>'err'});
# <input class="err" id="person_status" name="person.status" type="checkbox" value="1"/>

radio_button

$fb->radio_button($attribute, $value);
$fb->radio_button($attribute, $value, \%options);

Generate an HTML input type 'radio', typically part of a group including 2 or more controls. Generated value attributes uses $value, the control is marked 'checked' when $value matches the value of $attribute (or you can override, see below). \%options are HTML attributes which are passed to the tag builder unless special as described below.

Special \%options:

errors_classes

A string that is appended to the class attribute if the $attribute has errors (as defined by the model API)

checked

A boolean which determines if the input radio control is marked 'checked'. Used if you want to override the default.

Examples:

# Example radio group

$person->type('admin');

$fb->radio_button('type', 'admin');
$fb->radio_button('type', 'user');
$fb->radio_button('type', 'guest');

#<input checked id="person_type_admin" name="person.type" type="radio" value="admin"/>
#<input id="person_type_user" name="person.type" type="radio" value="user"/>
#<input id="person_type_guest" name="person.type" type="radio" value="guest"/>

# Example \%options

$fb->radio_button('type', 'guest', {class=>'foo', errors_classes=>'err'});
# <input class="foo err" id="person_type_guest" name="person.type" type="radio" value="guest"/>

$fb->radio_button('type', 'guest', {checked=>1});
# <input checked id="person_type_guest" name="person.type" type="radio" value="guest"/>

date_field

$fb->date_field($attribute);
$fb->date_field($attribute, \%options);

Generates a 'type' date HTML input control. Used when your $attribute value is a DateTime object to get proper string formatting. Although the date type is considered HTML5 you can use this for older HTML versions as well when you need to get the object formatting (you just don't get the date HTML controls).

When the $attribute value is a DateTime object (or actually is any object) we call '->ymd' to stringify the object to the expected format. '\%options' as in the input control are passed to the tag builder to create HTML attributes on the input tag with the exception of the specials ones already documented (such as errors_classes) and the following special \%options

min
max

When these are DateTime objects we stringify using ->ymd to get the expected format; otherwise they are passed as is to the tag builder.

Examples:

$person->birthday(DateTime->new(year=>1969, month=>2, day=>13));

$fb->date_field('birthday');
# <input id="person_birthday" name="person.birthday" type="date" value="1969-02-13"/>

$fb->date_field('birthday', {class=>'foo', errors_classes=>'err'});
# <input class="foo err" id="person_birthday" name="person.birthday" type="date" value="1969-02-13"/>

$fb->date_field('birthday', +{
  min => DateTime->new(year=>1900, month=>1, day=>1),
  max => DateTime->new(year=>2030, month=>1, day=>1),
});
#<input id="person_birthday" max="2030-01-01" min="1900-01-01" name="person.birthday" type="date" value="1969-02-13"/>

datetime_local_field

time_field

Like "date_field" but sets the input type to datetime-local or time respectively and formats any <DateTime> values with "->strftime('%Y-%m-%dT%T')" (for datetime-local) or either "->strftime('%H:%M')" or "->strftime('%T.%3N')" for time depending on the \%options include_seconds, which defaults to true).

Examples:

$person->due(DateTime->new(year=>1969, month=>2, day=>13, hour=>10, minute=>45, second=>11, nanosecond=> 500000000));

$fb->datetime_local_field('due');
# <input id="person_due" name="person.due" type="datetime-local" value="1969-02-13T10:45:11"/>

$fb->time_field('due');
# <input id="person_due" name="person.due" type="time" value="10:45:11.500"/>

$fb->time_field('due', +{include_seconds=>0});
# <input id="person_due" name="person.due" type="time" value="10:45"/>

submit

$fb->submit;
$fb->submit(\%options);
$fb->submit($value);
$fb->submit($value, \%options);

Create an HTML submit input tag with a meaningful default value based on the model name and its state in storage (if supported by the model). Will also look up the following two translation tag keys:

"formbuilder.submit.@{[ $self->name ]}.${key}"
"formbuilder.submit.${key}"

Where $key is by default 'submit' and if the model supports 'in_storage' its either 'update' or 'create' depending on if the model is new or already existing.

Examples:

$fb->submit;
# <input id="commit" name="commit" type="submit" value="Submit Person"/>

$fb->submit('Login', {class=>'foo'});
# <input class="foo" id="commit" name="commit" type="submit" value="Login"/>

button

$fb->button($name, \%attrs, \&block)
$fb->button($name, \%attrs, $content)
$fb->button($name, \&block)
$fb->button($name, $content)

Create a button tag with custom attibutes and content. Content be be a string or a coderef if you need to do complex layout.

Useful to create submit buttons with fancy formatting or when you need a button that submits in the form namespace.

Examples:

$person->type('admin');

$fb->button('type');
# <button id="person_type" name="person.type" type="submit" value="admin">Button</button>

$fb->button('type', {class=>'foo'});
# <button class="foo" id="person_type" name="person.type" type="submit" value="admin">Button</button>

$fb->button('type', "Press Me")
# <button id="person_type" name="person.type" type="submit" value="admin">Press Me</button>

$fb->button('type', sub { "Press Me" })
# <button id="person_type" name="person.type" type="submit" value="admin">Press Me</button>

legend

$fb->legend;
$fb->legend(\%options);
$fb->legend($content);
$fb->legend($content, \%options);
$fb->legend(\&template);
$fb->legend(\%options, \&template);

Create an HTML Form legend element with default content that is based on the model name. Accepts \%options which are passed to the tag builder and used to create HTML element attributes. You can override the content with either a $content string or a \&template coderef (which will receive the default content translation as its first argument).

The default content will be based on the model name and can be influenced by its storage status if in_storage is supplied by the model. We attempt to lookup the content string via the following translation tags (if the body supports ->i18n):

"formbuilder.legend.@{[ $self->name ]}.${key}"
"formbuilder.legend.${key}"

Where $key is 'new' if the model doesn't support in_storage else it's either 'update' or 'create' based on if the current model is already in storage (update) or its new and needs to be created.

Examples:

$fb->legend;
# <legend>New Person</legend>

$fb->legend({class=>'foo'});
# <legend class="foo">New Person</legend>

$fb->legend("Person");
# <legend>Person</legend>

$fb->legend("Persons", {class=>'foo'});
# <legend class="foo">Persons</legend>

$fb->legend(sub { shift . " Info"});
# <legend>New Person Info</legend>

$fb->legend({class=>'foo'}, sub {"Person"});
# <legend class="foo">Person</legend>

legend_for

$fb->legend_for($attr);
$fb->legend_for($attr, \%options);

Creates an HTML legend tags with it's content set to the human translated name of the given $attribute. Allows you to pass some additional HTML attributes to the legend tag. Examples:

$fb->legend_for('status')
# <legend id="status_legend" >Status</legend>

$fb->legend_for('status', {class=>'foo'})
# <legend id="status_legend" class="foo" >Status</legend>

fields_for

$fb->fields_for($attribute, sub {
  my ($nested_fb, $model) = @_;
});

$fb->fields_for($attribute, \%options, sub {
  my ($nested_fb, $model) = @_;
});

# With a 'finally' block when $attribute is a collection

$fb->fields_for($attribute, sub {
  my ($nested_fb, $model) = @_;
}, sub {
  my ($nested_fb, $new_model) = @_;
});

Used to create sub form builders under the current one for nested models (either a collection of models or a single model.) This sub form builder will be passed as the first argument to the enclosing subref and will encapsulate any indexing or namespacing; its model will be set to the sub model. You also get a second argument which is the sub model for ease of access. Note that if the $attribute refers to a collection then $model will be set to the current item model of that collection.

When the $attribute refers to a collection the collection object must provide a next method which should iterate thru the collection in the order desired and return undef to indicate all records have been rolled thru. This collection object may also implement a reset method to return the index to the start of the collection (which will be called after the final record is processed) and a build method which should return a new empty record (required if you want a finally block as described below).

Please see Valiant::HTML::Util::Collection for example. NOTE: If you supply an arrayref instead of a collection object, we will build one using Valiant::HTML::Util::Collection automatically. This behavior might change in the future so it would be ideal to not rely on it.

If the $attribute is a collection you may optionally add a second coderef template which is called after the collect has been fully iterated thru and it recieves a sub formbuilder with a new blank model as an argument. This finally block is always called, even if the collection is empty so it can he used to generate a blank entry for adding new items to the collection (for example) or for any extra code or field controls that you want under the sub model namespace.

Available \%options:

builder

The class name of the formbuilder. Defaults to Valiant::HTML::FormBuilder or whatever the current builder is (if overridden in the parent).

namespace

The ID namespace. Will default to the parent formbuilder namespace if there is one.

child_index

The index of the sub object. Can be a coderef. Used if you need explicit control over the index generated

include_id

Defaults to true. If the sub model does in_storage and primary_columns then add hidden form fields with those IDs to the sub model namespace. Often needed to properly match a record to its existing state in storage (such as a database). Not sure why you'd want to turn this off but the option is a carry over from Rails so I presume there is a use case.

id

Override the ID namespace for th sub model.

index

Explicitly override the index of the sub model.

Example of an attribute that refers to a nested object.

$person->profile(Local::Profile->new(zip=>'78621', address=>'ab'));

$fb->fields_for('profile', sub {
  my $fb_profile = shift;
  return  $fb_profile->input('address'),
          $fb_profile->errors_for('address'),
          $fb_profile->input('zip');
});

# <input id="person_profile_address" name="person.profile.address" type="text" value="ab"/>
# <div>Address is too short (minimum is 3 characters)</div>
# <input id="person_profile_zip" name="person.profile.zip" type="text" value="78621"/>

Example of an attribute that refers to a nested collection object (and with a "finally block")

$person->credit_cards([
  Local::CreditCard->new(number=>'234234223444', expiration=>DateTime->now->add(months=>11)),
  Local::CreditCard->new(number=>'342342342322', expiration=>DateTime->now->add(months=>11)),
  Local::CreditCard->new(number=>'111112222233', expiration=>DateTime->now->subtract(months=>11)),  # An expired card
]);

$fb->fields_for('credit_cards', sub {
  my $fb_cc = shift;
  return  $fb_cc->input('number'),
          $fb_cc->date_field('expiration'),
          $fb_cc->errors_for('expiration');
}, sub {
  my $fb_finally = shift;
  return  $fb_finally->button('add', +{value=>1}, 'Add a New Credit Card');
});

# <input id="person_credit_cards_0_number" name="person.credit_cards[0].number" type="text" value="234234223444"/>
# <input id="person_credit_cards_0_expiration" name="person.credit_cards[0].expiration" type="date" value="2023-01-23"/>
# <input id="person_credit_cards_1_number" name="person.credit_cards[1].number" type="text" value="342342342322"/>
# <input id="person_credit_cards_1_expiration" name="person.credit_cards[1].expiration" type="date" value="2023-01-23"/>
# <input id="person_credit_cards_2_number" name="person.credit_cards[2].number" type="text" value="111112222233"/>
# <input id="person_credit_cards_2_expiration" name="person.credit_cards[2].expiration" type="date" value="2021-03-23"/>
# <div>Expiration chosen date can&#39;t be earlier than 2022-02-23</div>
# <button id="person_credit_cards_3_add" name="person.credit_cards[3].add" type="submit" value="1">Add a New Credit Card</button>

select

$fb->select($attribute_proto, \@options, \%options)
$fb->select($attribute_proto, \@options)
$fb->select($attribute_proto, \%options, \&template)
$fb->select($attribute_proto, \&template)

Where $attribute_proto is one of:

$attribute                # A scalar value which is an attribute on the underlying $model
{ $attribute => $method } # A hashref composed of an $attribute on the underlying $model
                          # which returns a sub model or a collection of sub models
                          # and a $method to be called on the value of that sub model (
                          # or on each item sub model if the $attribute is a collection).

Used to create a select tag group with option tags. \@options can be anything that can be accepted by "options_for_select" in Valiant::HTML::FormTags. The value(s) of $attribute_proto are automatically marked as selected.

Since this is built on top if select_tag \%options can be anything supported by that method. See "select_tag" in Valiant::HTML::FormTags for more. In addition we have the following special handling for \%options:

selected
disabled

Mark C\<option> tags as selected or disabled. If you manual set selected then we ignore the value of $attribute (or @values when $attribute is a collection)

unselected_value

The value to set the hidden 'unselected' field to. No default value. See 'include_hidden' for details.

include_hidden

Defaults to true for 'multiple' and false for single type selects.

The rules for an HTML form select field specify that if the no option is 'selected' then nothing is submitted. This can cause issues if you are expecting a submission that somehow indicates 'nothing selected' means to unset some settings. So we do one of two things when 'include_hidden' is true. When the select is a simple 'single value' select (not multiple) we add a hidden field with the same name as the select name but indexed to 0 so its always the first value; its value is whatever you set 'unselected_value' to. If you don't set 'unselected_value' this hidden field is NOT created. If you are using Plack::Request or Mojolicious (or using Catalyst with use_hash_multivalue_in_request option set to true, or something like Catalyst::TraitFor::Request::StructuredParameters) then the last value of an array body parameter will be returned which will let you choose between a default value or an actual returned value. Example:

# $fb->select('state_ids', [map { [$_->label, $_->id] } $roles_collection->all], +{include_hidden=>1, unselected_value=>-1} );
# <input id="person_state_ids_hidden" name="person.state_ids[0]" type="hidden" value="-1"/>
# <select id="person_state_ids" multiple name="person.state_ids[]">
#   <option selected value="1">user</option>
#   <option value="2">admin</option>
#   <option selected value="3">guest</option>
# </select>

If you've set the 'multiple' attribute to true, or we detect that multiple values are intended (either when the form value of the field is an arrayref or a collection) indicting your select drop list allows one to choose more than one option, we add a hidden field '_nop' at index 0 which you will need to treat at the signal for 'this means unset. If you are using this with DBIx:Class::Valiant then that code will automatically handle this for you. Otherwise you'll need to handle it manually or add code to detect that there is no form submission value under that name. If you don't want this behavior you can manually turn it off be explicitly setting 'include_hidden' to false.

Optionally you can provide a \&template which should return option tags. This coderef will recieve the $model, $attribute and an array of @selected values based on the $attribute.

Examples:

$fb->select('state_id', [1,2,3], +{class=>'foo'} );
# <select class="foo" id="person_state_id" name="person.state_id">
#   <option selected value="1">1</option>
#   <option value="2">2</option>
#   <option value="3">3</option>
# </select>

$fb->select('state_id', [1,2,3], +{selected=>[3], disabled=>[1]} );
# <select id="person_state_id" name="person.state_id">
#   <option disabled value="1">1</option>
#   <option value="2">2</option>
#   <option selected value="3">3</option>
# </select>

$fb->select('state_id', [map { [$_->name, $_->id] } $states_collection->all], +{include_blank=>1} );
# <select id="person_state_id" name="person.state_id">
#   <option label=" " value=""></option>
#   <option selected value="1">TX</option>
#   <option value="2">NY</option>
#   <option value="3">CA</option>
# </select>

$fb->select('state_id', sub {
  my ($model, $attribute, $value) = @_;
  return map {
    my $selected = $_->id eq $value ? 1:0;
    option_tag($_->name, +{class=>'foo', selected=>$selected, value=>$_->id}); 
  } $states_collection->all;
});
# <select id="person_state_id" name="person.state_id">
#   <option class="foo" selected value="1">TX</option>
#   <option class="foo" value="2">NY</option>
#   <option class="foo" value="3">CA</option>
# </select>

Examples when $attribute is a collection:

$fb->select({roles => 'id'}, [map { [$_->label, $_->id] } $roles_collection->all]), 
# <input id="person_roles_id_hidden" name="person.roles[0]._nop" type="hidden" value="1"/>
# <select id="person_roles_id" multiple name="person.roles[].id">
#   <option selected value="1">user</option>
#   <option selected value="2">admin</option>
#   <option value="3">guest</option>
# </select>

Please note when the $attribute is a collection we add a hidden field to cope with case when no items are selected, you'll need to write form processing code to mark and notice the _nop field.

collection_select

$fb->collection_select($attribute_proto, $collection, $value_method, $text_method, \%options);
$fb->collection_select($attribute_proto, $collection, $value_method, $text_method);
$fb->collection_select($attribute_proto, $collection);

Where $attribute_proto is one of:

$attribute                # A string which is an attribute on the underlying $model
                          # that returns a scalar value.
{ $attribute => $method } # A hashref composed of an $attribute on the underlying $model
                          # which returns a sub model or a collection of sub models
                          # and a $method to be called on the value of that sub model (
                          # or on each item sub model if the $attribute is a collection).

Similar to "select" but works with a $collection instead of delineated options. The collection can be an actual collection object, or the string name of a method on the model which provides the actual collection objection. Its a type of shortcut to reduce boilerplate at the expense of some flexibility ( if you need that you'll need to use "select"). Examples:

$fb->collection_select('state_id', $states_collection, id=>'name');
# <select id="person.state_id" name="person.state_id">
#   <option selected value="1">TX</option>
#   <option value="2">NY</option>
#   <option value="3">CA</option>
# </select>

$fb->collection_select('state_id', $states_collection, id=>'name', {class=>'foo', include_blank=>1});
# <select class="foo" id="person.state_id" name="person.state_id">
#   <option label=" " value=""></option>
#   <option selected value="1">TX</option>
#   <option value="2">NY</option>
#   <option value="3">CA</option>
# </select>

$fb->collection_select('state_id', $states_collection, id=>'name', {selected=>[3], disabled=>[1]});
# <select id="person.state_id" name="person.state_id">
#   <option disabled value="1">TX</option>
#   <option value="2">NY</option>
#   <option selected value="3">CA</option>
# </select>

is $fb->collection_select({roles => 'id'}, $roles_collection, id=>'label');
# <input id="person_roles_id_hidden" name="person.roles[0]._nop" type="hidden" value="1"/>
# <select id="person_roles_id" multiple name="person.roles[].id">
#   <option selected value="1">user</option>
#   <option selected value="2">admin</option>
#   <option value="3">guest</option>
# </select>

Please note when the $attribute is a collection value we add a hidden field to allow you to send a signal to the form processor that this namespace contains no records. Otherwise the form will just send nothing. If you have a custom way to handle this you can disable the behavior if you wish by explicitly setting include_hidden=>0

collection_checkbox

$fb->collection_checkbox({$attribute=>$value_method}, $collection, $value_method, $text_method, \%options);
$fb->collection_checkbox({$attribute=>$value_method}, $collection, $value_method, $text_method);
$fb->collection_checkbox({$attribute=>$value_method}, $collection);
$fb->collection_checkbox({$attribute=>$value_method}, $collection, $value_method, $text_method, \%options, \&template);
$fb->collection_checkbox({$attribute=>$value_method}, $collection, $value_method, $text_method, \&template);
$fb->collection_checkbox({$attribute=>$value_method}, $collection, \&template);

Create a checkbox group for a collection attribute

Examples:

Where the $attribute roles refers to a collection of sub models, each of which provides a method id which is used to fetch a matching value and $roles_collection refers to the full set of available roles which can be added or removed from the parent model.

In these examples $collection and $roles_collection can be either a collection object or a string which is the method name on the current model which provides the collection.

$fb->collection_checkbox({roles => 'id'}, $roles_collection, id=>'label'); 
# <input id="person_roles_0__nop" name="person.roles[0]._nop" type="hidden" value="1"/>
# <label for="person_roles_1_id">user</label>
# <input checked id="person_roles_1_id" name="person.roles[1].id" type="checkbox" value="1"/>
# <label for="person_roles_2_id">admin</label>
# <input checked id="person_roles_2_id" name="person.roles[2].id" type="checkbox" value="2"/>
# <label for="person_roles_3_id">guest</label>
# <input id="person_roles_3_id" name="person.roles[3].id" type="checkbox" value="3"/>

Please note when the $attribute is a collection value we add a hidden field to allow you to send a signal to the form processor that this namespace contains no records. Otherwise the form will just send nothing. If you have a custom way to handle this you can disable the behavior if you wish by explicitly setting include_hidden=>0

If you have special needs for formatting or layout you can override the default template with a coderef that will receive a special type of formbuilder localized to the current value (an instance of Valiant::HTML::FormBuilder::Checkbox):

$fb->collection_checkbox({roles => 'id'}, $roles_collection, id=>'label', sub {
  my $fb_roles = shift;
  return  $fb_roles->checkbox({class=>'form-check-input'}),
          $fb_roles->label({class=>'form-check-label'});
});

# <input id="person_roles_4__nop" name="person.roles[4]._nop" type="hidden" value="1"/>
# <input checked class="form-check-input" id="person_roles_5_id" name="person.roles[5].id" type="checkbox" value="1"/>
# <label class="form-check-label" for="person_roles_5_id">user</label>
# <input checked class="form-check-input" id="person_roles_6_id" name="person.roles[6].id" type="checkbox" value="2"/>
# <label class="form-check-label" for="person_roles_6_id">admin</label>
# <input class="form-check-input" id="person_roles_7_id" name="person.roles[7].id" type="checkbox" value="3"/>
# <label class="form-check-label" for="person_roles_7_id">guest</label>

In addition to overriding checkbox and label to already contain value and state (if its checked or not) information. This special builder contains some additional methods of possible use, you should see the documentation of Valiant::HTML::FormBuilder::Checkbox for more.

If provided %options is a hashref of the following optional values

include_hidden

Defaults to whatever the method default_collection_checkbox_include_hidden returns. In the core code the returns true. If true will include a hidden field set to the name of the collection, which is uses to indicate 'no checked values' since HTML will send nothing by default if there's no checked values. It will add this hidden field for each checkbox item to represent the 'off' value.

builder

Defaults to the values of DEFAULT_COLLECTION_CHECKBOX_BUILDER method. In core code this is Valiant::HTML::FormBuilder::Checkbox. Overide if you need to make a custom builder (tricky work).

collection_radio_buttons

$fb->collection_radio_buttons($attribute, $collection, $value_method, $text_method, \%options);
$fb->collection_radio_buttons($attribute, $collection, $value_method, $text_method);
$fb->collection_radio_buttons($attribute, $collection);
$fb->collection_radio_buttons($attribute, $collection, $value_method, $text_method, \%options, \&template);
$fb->collection_radio_buttons($attribute, $collection, $value_method, $text_method, \&template);
$fb->collection_radio_buttons($attribute, $collection, \&template);

A collection of radio buttons. Similar to \collection_checkbox but used one only one value is permitted. Example:

$fb->collection_radio_buttons('state_id', $states_collection, id=>'name');
# <input id="person_state_id_hidden" name="person.state_id" type="hidden" value=""/>
# <label for="person_state_id_1">TX</label>
# <input checked id="person_state_id_1_1" name="person.state_id" type="radio" value="1"/>
# <label for="person_state_id_2">NY</label>
# <input id="person_state_id_2_2" name="person.state_id" type="radio" value="2"/>
# <label for="person_state_id_3">CA</label>
# <input id="person_state_id_3_3" name="person.state_id" type="radio" value="3"/>

Please note when the $attribute is a collection value we add a hidden field to allow you to send a signal to the form processor that this namespace contains no records. Otherwise the form will just send nothing. If you have a custom way to handle this you can disable the behavior if you wish by explicitly setting include_hidden=>0

If you have special needs for formatting or layout you can override the default template with a coderef that will receive a special type of formbuilder localized to the current value (an instance of Valiant::HTML::FormBuilder::RadioButton):

$fb->collection_radio_buttons('state_id', $states_collection, id=>'name', sub {
  my $fb_states = shift;
  return  $fb_states->radio_button({class=>'form-check-input'}),
          $fb_states->label({class=>'form-check-label'});  
});
# <div id='person_state_id'>
#   <input id="person_state_id_hidden" name="person.state_id" type="hidden" value=""/>
#   <input checked class="form-check-input" id="person_state_id_1_1" name="person.state_id" type="radio" value="1"/>
#   <label class="form-check-label" for="person_state_id_1">TX</label>
#   <input class="form-check-input" id="person_state_id_2_2" name="person.state_id" type="radio" value="2"/>
#   <label class="form-check-label" for="person_state_id_2">NY</label>
#   <input class="form-check-input" id="person_state_id_3_3" name="person.state_id" type="radio" value="3"/>
#   <label class="form-check-label" for="person_state_id_3">CA</label>
# </div>

In addition to overriding radio_button and label to already contain value and state (if its checked or not) information. This special builder contains some additional methods of possible use, you should see the documentation of Valiant::HTML::FormBuilder::RadioButton for more.

Please note that the generated radio inputs will be wrapped in a containing div tag. You can change this tag using the container_tag option. For example:

$fb->collection_radio_buttons('state_id', $states_collection, id=>'name', +{container_tag=>'span'}, sub {
  my $fb_states = shift;
  return  $fb_states->radio_button({class=>'form-check-input'}),
          $fb_states->label({class=>'form-check-label'});  
});

Here's all the values for the '%options' argument. Any options that are not one of these will be passed to the container tag as html attributes:

checked_value

This is the current value of the attribute. By default its the attribute value (via \tag_value_for_attribute) but you can override as needed.

include_hidden.

Defaults to true. The value returned if you don't check one of the radio buttons.

container_tag

The tag that contains the generated radio buttons.

builder

The builder subclass used in the radio input generator.

radio_buttons

$fb->radio_buttons($attribute, \@options, \%options);
$fb->radio_buttons($attribute, \@options, \%options, \&template);
$fb->radio_buttons($attribute, \@options);
$fb->radio_buttons($attribute, \@options, \&template);

Similar to "collection_radio_buttons" but takes an arrayref of label / values instead of a collection. Useful when you have a list of radio buttons (like from an ENUM) but you don't want to list each radio separately. Example:

$fb_profile->radio_buttons('status', [[Pending=>'pending'],[Active=>'active'],[Inactive=>'inactive']]);

# <input id="person_profile_status_hidden" name="person.profile.status" type="hidden" value="">
# <input id="person_profile_status_pending_pending" name="person.profile.status" type="radio" value="pending">
# <label for="person_profile_status_pending">Pending</label>
# <input checked="" id="person_profile_status_active_actie" name="person.profile.status" type="radio" value="active">
# <label for="person_profile_status_active">Active</label>
# <input id="person_profile_status_inactive_inactive" name="person.profile.status" type="radio" value="inactive">
# <label for="person_profile_status_inactive">Inactive</label>

Supports using a template subroutine reference (like "collection_radio_buttons") when you need to be fussy about style and positioning.

SEE ALSO

Valiant

AUTHOR

See Valiant

COPYRIGHT & LICENSE

See Valiant

2 POD Errors

The following errors were encountered while parsing the POD:

Around line 2006:

Unknown directive: =over4

Around line 2008:

'=item' outside of any '=over'