| HTML-FormFu documentation | Contained in the HTML-FormFu distribution. |
HTML::FormFu::Element::Date - 3 select menu multi-field
---
elements:
- type: Date
name: birthdate
label: 'Birthdate:'
day:
prefix: "- Day -"
month:
prefix: "- Month -"
year:
prefix: "- Year -"
less: 70
plus: 0
auto_inflate: 1
Creates a multi element containing 3 select menus for the day, month and year.
A date element named foo would result in 3 select menus with the names
foo_day, foo_month and foo_year. The names can instead be
overridden by the name value in day, month and year.
This element automatically merges the input parameters from the select menu into a single date parameter (and doesn't delete the individual menu's parameters).
Arguments: DateTime object
Arguments: $date_string
Accepts either a DateTime object, or a string containing a date, matching the strftime format. Overwrites any default value set in day, month or year.
Arguments: $date_string
- type: Date
default_natural: 'today'
Accepts a date/time string suitable for passing to parse_datetime in DateTime::Format::Natural.
- type: Date
default_natural: 'today'
default_datetime_args:
set_time_zone: 'Europe/London'
Accepts a hashref of method-names / values that will be called on the default DateTime object, before the select fields' values are set from it.
Default Value: "%d-%m-%Y"
The format of the date as returned by params in HTML::FormFu, if auto_inflate is not set.
If auto_inflate is used, this is still the format that the parameter will be in prior to the DateTime inflator being run; which is what any Filters and Constraints will receive.
Arguments: \%setting
Set values effecting the day select menu. Known keys are:
Override the auto-generated name of the select menu.
Set the default value of the select menu
Arguments: $value
Arguments: \@values
A string or arrayref of strings to be inserted into the start of the select menu.
Each value is only used as the label for a select item - the value for each
of these items is always the empty string ''.
Arguments: \%setting
Set values effecting the month select menu. Known keys are:
Override the auto-generated name of the select menu.
Set the default value of the select menu
Arguments: $value
Arguments: \@values
A string or arrayref of strings to be inserted into the start of the select menu.
Each value is only used as the label for a select item - the value for each
of these items is always the empty string ''.
Arguments: \@months
A list of month names used for the month menu.
If not set, the list of month names is obtained from DateTime::Locale using the locale set in languages in HTML::FormFu.
Argument: bool
If true (and months is not set) the list of abbreviated month names is
obtained from DateTime::Locale using the locale set in
languages in HTML::FormFu.
Arguments: \%setting
Set values effecting the year select menu. Known keys are:
Override the auto-generated name of the select menu.
Set the default value of the select menu
Arguments: $value
Arguments: \@values
A string or arrayref of strings to be inserted into the start of the select menu.
Each value is only used as the label for a select item - the value for each
of these items is always the empty string ''.
Arguments: \@years
A list of years used for the year menu.
If this is set, reference, less and plus are ignored.
Arguments: $year
Default Value: the current year, calculated from time() (time() in perlfunc)
If list is not set, the list is created from the range of
reference - year_less to reference + year_plus.
Arguments: $count
Default Value: 0
Arguments: $count
Default Value: 10
Arguments: bool
Default Value: 0
If true, the list of years is listed in reverse (decreasing) order.
Arguments: \@fields
Default Value: ['day', 'month', 'year']
Specify the order of the date fields in the rendered HTML.
Not all 3 fields are required. No single field can be used more than once.
If true, a DateTime Inflator will automatically be added to the element, and it will be given a formatter so that stringification will result in the format specified in strftime.
If you require the DateTime Inflator to have a different stringification format to the format used internally by your Filters and Constraints, then you must explicitly add your own DateTime Inflator, rather than using auto_inflate.
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 select-menu 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::_Field, 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::Date; use Moose; extends 'HTML::FormFu::Element::Multi'; use HTML::FormFu::Util qw( _filter_components _parse_args ); use DateTime; use DateTime::Format::Builder; use DateTime::Format::Natural; use DateTime::Locale; use Moose::Util qw( apply_all_roles ); use Scalar::Util qw( blessed ); use List::MoreUtils qw( all none uniq ); use Carp qw( croak ); __PACKAGE__->mk_attrs(qw( day month year )); has auto_inflate => ( is => 'rw', traits => ['Chained'] ); has default_natural => ( is => 'rw', traits => ['Chained'] ); has default_datetime_args => ( is => 'rw', traits => ['Chained'] ); has printf_day => ( is => 'rw', traits => ['Chained'] ); has printf_month => ( is => 'rw', traits => ['Chained'] ); has printf_year => ( is => 'rw', traits => ['Chained'] ); has _known_fields => ( is => 'rw' ); has strftime => ( is => 'rw', default => '%d-%m-%Y', lazy => 1, traits => ['Chained'], ); *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->printf_day( '%d' ); $self->printf_month( '%d' ); $self->printf_year( '%d' ); $self->_known_fields( [qw( day month year )] ); $self->field_order( [qw( day month year )] ); $self->day( { prefix => [], } ); $self->month( { prefix => [], } ); $self->year( { prefix => [], less => 0, plus => 10, reverse => 0, } ); return; }; sub value { my ( $self, $value ) = @_; if ( @_ > 1 ) { $self->{value} = $value; # if we're already built - i.e. process() has ben called, # call default() on our children if ( @{ $self->_elements } ) { $self->_date_defaults; my @order = @{ $self->field_order }; for my $i ( 0 .. $#order ) { my $field = $order[$i]; my $printf_method = "printf_$field"; my $default = $value ? sprintf( $self->$printf_method, $self->$field->{default} ) : undef; $self->_elements->[$i]->default($default); } } return $self; } return $self->{value}; } sub _add_elements { my ($self) = @_; $self->_elements( [] ); $self->_date_defaults; for my $order ( @{ $self->field_order } ) { my $method = "_add_$order"; $self->$method; } if ( $self->auto_inflate && !@{ $self->get_inflators( { type => "DateTime" } ) } ) { _add_inflator($self); } return; } sub _date_defaults { my ($self) = @_; my $default; if ( defined( $default = $self->default ) && length $default ) { if ( !$self->form->submitted || $self->render_processed_value ) { for my $deflator ( @{ $self->_deflators } ) { $default = $deflator->process($default); } } my $is_blessed = blessed($default); if ( !$is_blessed || ( $is_blessed && !$default->isa('DateTime') ) ) { my $builder = DateTime::Format::Builder->new; $builder->parser( { strptime => $self->strftime } ); $default = $builder->parse_datetime($default); } } elsif ( defined( $default = $self->default_natural ) ) { my $parser; if ( defined( my $datetime_args = $self->default_datetime_args ) ) { if ( exists $datetime_args->{set_time_zone} ) { my $tz = $datetime_args->{set_time_zone}; $parser = DateTime::Format::Natural->new( time_zone => $tz ); } else { $parser = DateTime::Format::Natural->new; } } else { $parser = DateTime::Format::Natural->new; } $default = $parser->parse_datetime( $default ); } else { $default = undef; } if ( defined $default ) { if ( defined( my $datetime_args = $self->default_datetime_args ) ) { for my $key ( keys %$datetime_args ) { $default->$key( $datetime_args->{$key} ); } } for my $field ( @{ $self->field_order } ) { $self->$field->{default} = $default->$field; } } return; } sub _add_day { my ($self) = @_; my $day = $self->day; my $day_name = $self->_build_name('day'); my @day_prefix = ref $day->{prefix} ? @{ $day->{prefix} } : $day->{prefix}; @day_prefix = map { [ '', $_ ] } @day_prefix; my $element = $self->element( { type => 'Select', name => $day_name, options => [ @day_prefix, map { [ $_, $_ ] } 1 .. 31 ], defined $day->{default} ? ( default => $day->{default} ) : (), } ); apply_all_roles( $element, 'HTML::FormFu::Role::Element::MultiElement' ); return; } sub _add_month { my ($self) = @_; my $month = $self->month; my $month_name = $self->_build_name('month'); my @months = _build_month_list($self); my @month_prefix = ref $month->{prefix} ? @{ $month->{prefix} } : $month->{prefix}; @month_prefix = map { [ '', $_ ] } @month_prefix; my $options = [ @month_prefix, map { [ $_ + 1, $months[$_] ] } 0 .. 11 ]; my $element = $self->element( { type => 'Select', name => $month_name, options => $options, defined $month->{default} ? ( default => $month->{default} ) : (), } ); apply_all_roles( $element, 'HTML::FormFu::Role::Element::MultiElement' ); return; } sub _add_year { my ($self) = @_; my $year = $self->year; my $year_name = $self->_build_name('year'); my $year_ref = defined $year->{reference} ? $year->{reference} : ( localtime(time) )[5] + 1900; my @years = defined $year->{list} ? @{ $year->{list} } : ( $year_ref - $year->{less} ) .. ( $year_ref + $year->{plus} ); if ( $year->{reverse} ) { @years = reverse(@years); } my @year_prefix = ref $year->{prefix} ? @{ $year->{prefix} } : $year->{prefix}; @year_prefix = map { [ '', $_ ] } @year_prefix; my $element = $self->element( { type => 'Select', name => $year_name, options => [ @year_prefix, map { [ $_, $_ ] } @years ], defined $year->{default} ? ( default => $year->{default} ) : (), } ); apply_all_roles( $element, 'HTML::FormFu::Role::Element::MultiElement' ); return; } sub _build_month_list { my ($self) = @_; my $month = $self->month; my @months; if ( defined $month->{names} ) { @months = @{ $month->{names} }; } else { my $languages = $self->form->languages; if ( ref $languages ne 'ARRAY' ) { $languages = [$languages]; } for my $lang (@$languages) { my $loc; eval { $loc = DateTime::Locale->load($lang) }; if ( !$@ ) { @months = $month->{short_names} ? @{ $loc->month_format_abbreviated } : @{ $loc->month_format_wide }; @months = map {ucfirst} @months; last; } } } return @months; } sub _build_number_list { my ( $self, $start, $end, $interval ) = @_; $interval ||= 1; my @list; for ( my $i = $start; $i <= $end; $i += $interval ) { push @list, $i; } return @list; } sub _build_name { my ( $self, $type ) = @_; my $name = defined $self->$type->{name} ? $self->$type->{name} : sprintf "%s_%s", $self->name, $type; return $name; } sub _add_inflator { my ($self) = @_; $self->inflator( { type => "DateTime", parser => { strptime => $self->strftime, }, strptime => $self->strftime, } ); return; } sub field_order { my ( $self, @order ) = @_; if ( @_ > 1 ) { if ( @order == 1 && ref( $order[0] ) eq 'ARRAY' ) { @order = @{ $order[0] }; } for my $field (@order) { croak "unknown field type: '$field'" if none { $field eq $_ } @{ $self->_known_fields }; } croak 'repeated field type' if scalar( uniq @order ) != scalar(@order); $self->{field_order} = \@order; return $self; } else { return $self->{field_order}; } } sub process { my ( $self, @args ) = @_; $self->_add_elements; return $self->SUPER::process(@args); } sub process_input { my ( $self, $input ) = @_; my %value; my @order = @{ $self->field_order }; for my $i ( 0 .. $#order ) { my $field = $order[$i]; my $name = $self->_elements->[$i]->nested_name; $value{$field} = $self->get_nested_hash_value( $input, $name ); } if ( ( all {defined} values %value ) && all {length} values %value ) { my $dt; eval { $dt = DateTime->new( map { $_, $value{$_} } keys %value ); }; my $value; if ($@) { $value = $self->strftime; } else { $value = $dt->strftime( $self->strftime ); } $self->set_nested_hash_value( $input, $self->nested_name, $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__