| HTML-FormFu documentation | Contained in the HTML-FormFu distribution. |
HTML::FormFu::Element::ComboBox - Select / Text hybrid
---
elements:
- type: ComboBox
name: answer
label: 'Select yes or no, or write an alternative:'
values:
- yes
- no
Creates a multi element containing a Select field and a Text field.
A ComboBox element named foo would result in a Select menu named
foo_select and a Text field named foo_text. The names can instead be
overridden by the name value in select and text.
If a value is submitted for the Text field, this will be used in preference to any submitted value for the Select menu.
You can access the submitted value by using the ComboBox's name:
my $value = $form->param_value('foo');
If the value matches one of the Select menu's options, that options will be selected. Otherwise, the Text field will use the value as its default.
See options in HTML::FormFu::Element::_Group for details.
See values in HTML::FormFu::Element::_Group for details.
See value_range in HTML::FormFu::Element::_Group for details.
See empty_first in HTML::FormFu::Element::_Group for details.
See empty_first_label in HTML::FormFu::Element::_Group for details.
Arguments: \%setting
Set values effecting the Select menu. Known keys are:
Override the auto-generated name of the select menu.
Arguments: \%setting
Set values effecting the Text field. Known keys are:
Override the auto-generated name of the select menu.
Although this element inherits from HTML::FormFu::Element::Block, its behaviour for the methods filters|HTML::FormFu/filters in filter (filters|HTML::FormFu/filters in filter), constraints|HTML::FormFu/constraints in constraint (constraints|HTML::FormFu/constraints in constraint), inflators|HTML::FormFu/inflators in inflator (inflators|HTML::FormFu/inflators in inflator), validators|HTML::FormFu/validators in validator (validators|HTML::FormFu/validators in validator) and transformers|HTML::FormFu/transformers in transformer (transformers|HTML::FormFu/transformers in transformer) is more like that of a field element (HTML::FormFu::Element::_Field), meaning all processors are added directly to the date element, not to its child elements.
This element's get_elements and
get_all_elements are inherited from
HTML::FormFu::Element::Block, and so have the same behaviour. However, it
overrides the get_fields|HTML::FormFu/get_fields method, such that it
returns both itself and its child elements.
Is a sub-class of, and inherits methods from HTML::FormFu::Element::Multi, HTML::FormFu::Element::Block, HTML::FormFu::Element
Carl Franks, cfranks@cpan.org
This library is free software, you can redistribute it and/or modify it under the same terms as Perl itself.
| HTML-FormFu documentation | Contained in the HTML-FormFu distribution. |
package HTML::FormFu::Element::ComboBox; use Moose; extends 'HTML::FormFu::Element::Multi'; with 'HTML::FormFu::Role::Element::ProcessOptionsFromModel'; use HTML::FormFu::Util qw( _filter_components _parse_args ); use List::MoreUtils qw( any ); use Moose::Util qw( apply_all_roles ); our @DEFER_TO_SELECT = qw( empty_first empty_first_label values value_range ); for my $name ( @DEFER_TO_SELECT ) { has $name => ( is => 'rw', traits => ['Chained'] ); } has select => ( is => 'rw', traits => ['Chained'], default => sub { {} } ); has text => ( is => 'rw', traits => ['Chained'], default => sub { {} } ); *default = \&value; ## build get_Xs methods for my $method ( qw( deflator filter constraint inflator validator transformer ) ) { my $sub = sub { my $self = shift; my %args = _parse_args(@_); my $get_method = "get_${method}s"; my $accessor = "_${method}s"; my @x = @{ $self->$accessor }; push @x, map { @{ $_->$get_method(@_) } } @{ $self->_elements }; return _filter_components( \%args, \@x ); }; my $name = __PACKAGE__ . "::get_${method}s"; no strict 'refs'; *{$name} = $sub; } after BUILD => sub { my ( $self, $args ) = @_; $self->multi_value( 1 ); $self->empty_first( 1 ); return; }; sub options { my ( $self, @args ) = @_; if (@args) { $self->{options} = @args == 1 ? $args[0] : \@args; return $self; } else { # we're being called as a getter! # are the child elements made yet? if ( !@{ $self->_elements } ) { # need to build the children, so we can return the select options $self->_add_elements; } return $self->_elements->[0]->options; } } sub value { my ( $self, $value ) = @_; if ( @_ > 1 ) { $self->{value} = $value; # if we're already built - i.e. process() has been called, # call default() on our children if ( @{ $self->_elements } ) { $self->_combobox_defaults; $self->_elements->[0]->default( $self->select->{default} ); $self->_elements->[1]->default( $self->text->{default} ); } return $self; } return $self->{value}; } sub _add_elements { my ($self) = @_; $self->_elements( [] ); $self->_add_select; $self->_add_text; $self->_combobox_defaults; return; } sub _combobox_defaults { my ($self) = @_; if ( defined( my $default = $self->default ) ) { if ( !$self->form->submitted || $self->render_processed_value ) { for my $deflator ( @{ $self->_deflators } ) { $default = $deflator->process($default); } } my $select_options = $self->_elements->[0]->options; if ( $default ne '' && any { $_->{value} eq $default } @$select_options ) { $self->select->{default} = $default; $self->text->{default} = undef; } else { $self->select->{default} = undef; $self->text->{default} = $default; } } return; } sub _add_select { my ($self) = @_; my $select = $self->select; my $select_name = _build_field_name( $self, 'select' ); my $select_element = $self->element( { type => 'Select', name => $select_name, } ); apply_all_roles( $select_element, 'HTML::FormFu::Role::Element::MultiElement' ); for my $method (@DEFER_TO_SELECT) { if ( defined( my $value = $self->$method ) ) { $select_element->$method($value); } } if ( !@{ $select_element->options } ) { # we need to access the hashkey directly, # otherwise we'll have a loop $select_element->options( $self->{options} ); } if ( defined( my $default = $select->{default} ) ) { $select_element->default($default); } return; } sub _add_text { my ($self) = @_; my $text = $self->text; my $text_name = _build_field_name( $self, 'text' ); my $text_element = $self->element( { type => 'Text', name => $text_name, } ); apply_all_roles( $text_element, 'HTML::FormFu::Role::Element::MultiElement' ); if ( defined( my $default = $text->{default} ) ) { $text_element->default($default); } return; } sub _build_field_name { my ( $self, $type ) = @_; my $options = $self->$type; my $name; if ( defined( my $default_name = $options->{name} ) ) { $name = $default_name; } else { $name = sprintf "%s_%s", $self->name, $type; } return $name; } sub process { my ( $self, @args ) = @_; $self->_process_options_from_model; $self->_add_elements; return $self->SUPER::process(@args); } sub process_input { my ( $self, $input ) = @_; my $select_name = _build_field_name( $self, 'select' ); my $text_name = _build_field_name( $self, 'text' ); $select_name = $self->get_element( { name => $select_name } )->nested_name; $text_name = $self->get_element( { name => $text_name } )->nested_name; my $select_value = $self->get_nested_hash_value( $input, $select_name ); my $text_value = $self->get_nested_hash_value( $input, $text_name ); if ( defined $text_value && length $text_value ) { $self->set_nested_hash_value( $input, $self->nested_name, $text_value, ); } elsif ( defined $select_value && length $select_value ) { $self->set_nested_hash_value( $input, $self->nested_name, $select_value, ); } return $self->SUPER::process_input($input); } sub render_data { return shift->render_data_non_recursive(@_); } sub render_data_non_recursive { my ( $self, $args ) = @_; my $render = $self->SUPER::render_data_non_recursive( { elements => [ map { $_->render_data } @{ $self->_elements } ], $args ? %$args : (), } ); return $render; } __PACKAGE__->meta->make_immutable; 1; __END__