File Coverage

blib/lib/HTML/FormFu/Element/ComboBox.pm
Criterion Covered Total %
statement 118 124 95.1
branch 25 32 78.1
condition 9 12 75.0
subroutine 22 22 100.0
pod 1 8 12.5
total 175 198 88.3


line stmt bran cond sub pod time code
1             package HTML::FormFu::Element::ComboBox;
2              
3 5     5   716 use strict;
  5         8  
  5         191  
4             our $VERSION = '2.05'; # VERSION
5              
6 5     5   18 use Moose;
  5         5  
  5         30  
7 5     5   22151 use MooseX::Attribute::FormFuChained;
  5         10  
  5         188  
8             extends 'HTML::FormFu::Element::Multi';
9              
10             with 'HTML::FormFu::Role::Element::ProcessOptionsFromModel';
11              
12 5     5   18 use HTML::FormFu::Util qw( _filter_components _parse_args );
  5         6  
  5         287  
13 5     5   18 use List::Util 1.33 qw( any );
  5         121  
  5         264  
14 5     5   22 use Moose::Util qw( apply_all_roles );
  5         8  
  5         35  
15              
16             our @DEFER_TO_SELECT = qw(
17             empty_first
18             empty_first_label
19             values
20             value_range
21             );
22              
23             for my $name (@DEFER_TO_SELECT) {
24             has $name => ( is => 'rw', traits => ['FormFuChained'] );
25             }
26              
27             has select => ( is => 'rw', traits => ['FormFuChained'], default => sub { {} } );
28             has text => ( is => 'rw', traits => ['FormFuChained'], default => sub { {} } );
29              
30             *default = \&value;
31              
32             ## build get_Xs methods
33             for my $method ( qw(
34             deflator filter
35             constraint inflator
36             validator transformer
37             ) )
38             {
39             my $sub = sub {
40 52     52   52 my $self = shift;
41 52         81 my %args = _parse_args(@_);
42 52         92 my $get_method = "get_${method}s";
43              
44 52         68 my $accessor = "_${method}s";
45 52         48 my @x = @{ $self->$accessor };
  52         1366  
46 52         49 push @x, map { @{ $_->$get_method(@_) } } @{ $self->_elements };
  104         78  
  104         379  
  52         1139  
47              
48 52         89 return _filter_components( \%args, \@x );
49             };
50              
51             my $name = __PACKAGE__ . "::get_${method}s";
52              
53             ## no critic (ProhibitNoStrict);
54 5     5   1740 no strict 'refs';
  5         8  
  5         4477  
55              
56             *{$name} = $sub;
57             }
58              
59             after BUILD => sub {
60             my ( $self, $args ) = @_;
61              
62             $self->multi_value(1);
63             $self->empty_first(1);
64              
65             return;
66             };
67              
68             sub options {
69 1     1 1 1 my ( $self, @args ) = @_;
70              
71 1 50       3 if (@args) {
72 1 50       3 $self->{options} = @args == 1 ? $args[0] : \@args;
73              
74 1         3 return $self;
75             }
76             else {
77              
78             # we're being called as a getter!
79             # are the child elements made yet?
80              
81 0 0       0 if ( !@{ $self->_elements } ) {
  0         0  
82              
83             # need to build the children, so we can return the select options
84 0         0 $self->_add_elements;
85             }
86              
87 0         0 return $self->_elements->[0]->options;
88             }
89             }
90              
91             sub value {
92 47     47 0 54 my ( $self, $value ) = @_;
93              
94 47 100       106 if ( @_ > 1 ) {
95 2         3 $self->{value} = $value;
96              
97             # if we're already built - i.e. process() has been called,
98             # call default() on our children
99              
100 2 50       4 if ( @{ $self->_elements } ) {
  2         49  
101 2         6 $self->_combobox_defaults;
102              
103 2         45 $self->_elements->[0]->default( $self->select->{default} );
104 2         60 $self->_elements->[1]->default( $self->text->{default} );
105             }
106              
107 2         4 return $self;
108             }
109              
110 45         104 return $self->{value};
111             }
112              
113             sub _add_elements {
114 15     15   21 my ($self) = @_;
115              
116 15         360 $self->_elements( [] );
117              
118 15         45 $self->_add_select;
119 15         54 $self->_add_text;
120              
121 15         50 $self->_combobox_defaults;
122              
123 15         21 return;
124             }
125              
126             sub _combobox_defaults {
127 17     17   28 my ($self) = @_;
128              
129 17 100       58 if ( defined( my $default = $self->default ) ) {
130              
131 7 100 66     33 if ( !$self->form->submitted || $self->render_processed_value ) {
132 5         7 for my $deflator ( @{ $self->_deflators } ) {
  5         123  
133 0         0 $default = $deflator->process($default);
134             }
135             }
136              
137 7         169 my $select_options = $self->_elements->[0]->options;
138              
139 7 100 66     74 if ( $default ne ''
140 24     24   52 && any { $_->{value} eq $default } @$select_options )
141             {
142 2         53 $self->select->{default} = $default;
143 2         48 $self->text->{default} = undef;
144             }
145             else {
146 5         134 $self->select->{default} = undef;
147 5         118 $self->text->{default} = $default;
148             }
149             }
150              
151 17         34 return;
152             }
153              
154             sub _add_select {
155 15     15   20 my ($self) = @_;
156              
157 15         364 my $select = $self->select;
158              
159 15         44 my $select_name = _build_field_name( $self, 'select' );
160              
161 15         101 my $select_element = $self->element( {
162             type => 'Select',
163             name => $select_name,
164             } );
165              
166 15         75 apply_all_roles( $select_element,
167             'HTML::FormFu::Role::Element::MultiElement' );
168              
169 15         141309 for my $method (@DEFER_TO_SELECT) {
170 60 100       1622 if ( defined( my $value = $self->$method ) ) {
171 27         472 $select_element->$method($value);
172             }
173             }
174              
175 15 100       19 if ( !@{ $select_element->options } ) {
  15         61  
176              
177             # we need to access the hashkey directly,
178             # otherwise we'll have a loop
179 3         9 $select_element->options( $self->{options} );
180             }
181              
182 15 100       52 if ( defined( my $default = $select->{default} ) ) {
183 1         4 $select_element->default($default);
184             }
185              
186 15         49 return;
187             }
188              
189             sub _add_text {
190 15     15   27 my ($self) = @_;
191              
192 15         379 my $text = $self->text;
193              
194 15         41 my $text_name = _build_field_name( $self, 'text' );
195              
196 15         87 my $text_element = $self->element( {
197             type => 'Text',
198             name => $text_name,
199             } );
200              
201 15         69 apply_all_roles( $text_element,
202             'HTML::FormFu::Role::Element::MultiElement' );
203              
204 15 100       139109 if ( defined( my $default = $text->{default} ) ) {
205 4         24 $text_element->default($default);
206             }
207              
208 15         35 return;
209             }
210              
211             sub get_select_field_nested_name {
212 11     11 0 13 my ($self) = @_;
213              
214 11         37 my $select_name = _build_field_name( $self, 'select' );
215              
216 11         79 return $self->get_element( { name => $select_name } )->nested_name;
217             }
218              
219             sub get_text_field_nested_name {
220 11     11 0 13 my ($self) = @_;
221              
222 11         29 my $text_name = _build_field_name( $self, 'text' );
223              
224 11         44 return $self->get_element( { name => $text_name } )->nested_name;
225             }
226              
227             sub _build_field_name {
228 52     52   64 my ( $self, $type ) = @_;
229              
230 52         1212 my $options = $self->$type;
231 52         48 my $name;
232              
233 52 50       119 if ( defined( my $default_name = $options->{name} ) ) {
234 0         0 $name = $default_name;
235             }
236             else {
237 52         139 $name = sprintf "%s_%s", $self->name, $type;
238             }
239              
240 52         85 return $name;
241             }
242              
243             sub process {
244 15     15 0 22 my ( $self, @args ) = @_;
245              
246 15         64 $self->_process_options_from_model;
247              
248 15         39 $self->_add_elements;
249              
250 15         111 return $self->SUPER::process(@args);
251             }
252              
253             sub process_input {
254 11     11 0 20 my ( $self, $input ) = @_;
255              
256 11         32 my $select_name = $self->get_select_field_nested_name;
257 11         42 my $text_name = $self->get_text_field_nested_name;
258              
259 11         64 my $select_value = $self->get_nested_hash_value( $input, $select_name );
260 11         29 my $text_value = $self->get_nested_hash_value( $input, $text_name );
261              
262 11 100 100     77 if ( defined $text_value && length $text_value ) {
    100 66        
263 4         13 $self->set_nested_hash_value( $input, $self->nested_name, $text_value,
264             );
265             }
266             elsif ( defined $select_value && length $select_value ) {
267 6         16 $self->set_nested_hash_value( $input, $self->nested_name, $select_value,
268             );
269             }
270              
271 11         51 return $self->SUPER::process_input($input);
272             }
273              
274             sub render_data {
275 6     6 0 19 return shift->render_data_non_recursive(@_);
276             }
277              
278             sub render_data_non_recursive {
279 6     6 0 8 my ( $self, $args ) = @_;
280              
281             my $render = $self->SUPER::render_data_non_recursive( {
282 6 50       11 elements => [ map { $_->render_data } @{ $self->_elements } ],
  12         43  
  6         169  
283             $args ? %$args : (),
284             } );
285              
286 6         18 return $render;
287             }
288              
289             __PACKAGE__->meta->make_immutable;
290              
291             1;
292              
293             __END__
294              
295             =head1 NAME
296              
297             HTML::FormFu::Element::ComboBox - Select / Text hybrid
298              
299             =head1 VERSION
300              
301             version 2.05
302              
303             =head1 SYNOPSIS
304              
305             ---
306             elements:
307             - type: ComboBox
308             name: answer
309             label: 'Select yes or no, or write an alternative:'
310             values:
311             - yes
312             - no
313              
314              
315             =head1 DESCRIPTION
316              
317             Creates a L<multi|HTML::FormFu::Element::Multi> element containing a Select
318             field and a Text field.
319              
320             A ComboBox element named C<foo> would result in a Select menu named
321             C<foo_select> and a Text field named C<foo_text>. The names can instead be
322             overridden by the C<name> value in L</select> and L</text>.
323              
324             If a value is submitted for the Text field, this will be used in preference
325             to any submitted value for the Select menu.
326              
327             You can access the submitted value by using the ComboBox's name:
328              
329             my $value = $form->param_value('foo');
330              
331             =head1 METHODS
332              
333             =head2 default
334              
335             If the value matches one of the Select menu's options, that options will be
336             selected. Otherwise, the Text field will use the value as its default.
337              
338             =head2 options
339              
340             See L<HTML::FormFu::Role::Element::Group/options> for details.
341              
342             =head2 values
343              
344             See L<HTML::FormFu::Role::Element::Group/values> for details.
345              
346             =head2 value_range
347              
348             See L<HTML::FormFu::Role::Element::Group/value_range> for details.
349              
350             =head2 empty_first
351              
352             See L<HTML::FormFu::Role::Element::Group/empty_first> for details.
353              
354             =head2 empty_first_label
355              
356             See L<HTML::FormFu::Role::Element::Group/empty_first_label> for details.
357              
358             =head2 select
359              
360             Arguments: \%setting
361              
362             Set values effecting the Select menu. Known keys are:
363              
364             =head3 name
365              
366             Override the auto-generated name of the select menu.
367              
368             =head2 text
369              
370             Arguments: \%setting
371              
372             Set values effecting the Text field. Known keys are:
373              
374             =head3 name
375              
376             Override the auto-generated name of the select menu.
377              
378             =head1 CAVEATS
379              
380             Although this element inherits from L<HTML::FormFu::Element::Block>, its
381             behaviour for the methods
382             L<filterE<sol>filters|HTML::FormFu/filters>,
383             L<constraintE<sol>constraints|HTML::FormFu/constraints>,
384             L<inflatorE<sol>inflators|HTML::FormFu/inflators>,
385             L<validatorE<sol>validators|HTML::FormFu/validators> and
386             L<transformerE<sol>transformers|HTML::FormFu/transformers> is more like that of
387             a L<field element|HTML::FormFu::Role::Element::Field>, meaning all processors are
388             added directly to the date element, not to its child elements.
389              
390             This element's L<get_elements|HTML::FormFu/get_elements> and
391             L<get_all_elements|HTML::FormFu/get_all_elements> are inherited from
392             L<HTML::FormFu::Element::Block>, and so have the same behaviour. However, it
393             overrides the C<get_fields|HTML::FormFu/get_fields> method, such that it
394             returns both itself and its child elements.
395              
396             =head1 SEE ALSO
397              
398             Is a sub-class of, and inherits methods from
399             L<HTML::FormFu::Element::Multi>,
400             L<HTML::FormFu::Element::Block>,
401             L<HTML::FormFu::Element>
402              
403             L<HTML::FormFu>
404              
405             =head1 AUTHOR
406              
407             Carl Franks, C<cfranks@cpan.org>
408              
409             =head1 LICENSE
410              
411             This library is free software, you can redistribute it and/or modify it under
412             the same terms as Perl itself.
413              
414             =cut