File Coverage

blib/lib/Data/MuForm/Renderer/Base.pm
Criterion Covered Total %
statement 405 431 93.9
branch 73 100 73.0
condition 50 93 53.7
subroutine 48 51 94.1
pod 9 48 18.7
total 585 723 80.9


line stmt bran cond sub pod time code
1             package Data::MuForm::Renderer::Base;
2             # ABSTRACT: Renderer
3              
4 91     91   52423 use Moo;
  91         136  
  91         546  
5 91     91   20680 use List::Util ('any');
  91         1471  
  91         7943  
6 91     91   399 use Scalar::Util ('weaken');
  91         114  
  91         312079  
7              
8              
9             has 'form' => ( is => 'ro', weak_ref => 1 );
10              
11             has 'localizer' => ( is => 'ro' );
12              
13             has 'standard_layout' => ( is => 'rw', default => 'lbl_ele_err' );
14              
15             has 'cb_layout' => ( is => 'rw', default => 'cbwrlr' );
16              
17             has 'rdgo_layout' => ( is => 'rw', default => 'labels_right' );
18              
19             has 'cbgo_layout' => ( is => 'rw', default => 'labels_right' );
20              
21             has 'display_layout' => ( is => 'rw', default => 'span' );
22              
23             has 'field_wrapper' => ( is => 'rw', default => 'simple' );
24              
25             has 'wrapper_tag' => ( is => 'rw', default => 'div' );
26              
27             has 'error_tag' => ( is => 'rw', default => 'span' );
28              
29             has 'error_class' => ( is => 'rw', default => 'error_message' );
30              
31             has 'render_element_errors' => ( is => 'rw', default => 1 );
32              
33             sub BUILD {
34 165     165 0 126403 my $self = shift;
35 165 100       1285 if ( $self->form ) {
36 132         2184 weaken($self->{localizer});
37             }
38             }
39              
40             sub render_hook {
41 216     216 0 180 my $self = shift;
42 216         677 return $self->form->render_hook($self, @_);
43             }
44              
45              
46             sub localize {
47 83     83 0 111 my ( $self, @message ) = @_;
48 83         256 return $self->localizer->loc_($message[0]);
49             }
50              
51             #==============================
52             # Forms
53             #==============================
54              
55             sub render_form {
56 6     6 0 18 my ($self, $rargs, $fields ) = @_;
57              
58 6         9 my $out = '';
59 6         18 $out .= $self->render_start($rargs);
60 6         21 $out .= $self->render_form_errors($rargs);
61              
62 6         13 foreach my $field ( @$fields ) {
63 16         119 $out .= $field->render;
64             }
65              
66 6         21 $out .= $self->render_end($rargs);
67 6         35 return $out;
68             }
69              
70             sub render_start {
71 7     7 0 20 my ($self, $rargs ) = @_;
72              
73 7         14 my $name = $rargs->{name};
74 7         9 my $method = $rargs->{method};
75 7         19 my $out = qq{<form };
76 7         19 $out .= qq{id="$name" };
77 7         15 $out .= qq{method="$method" };
78 7         17 $out .= q{>};
79             }
80              
81             sub render_end {
82 6     6 0 10 my ($self, $rargs ) = @_;
83              
84 6         12 return q{</form>};
85             }
86              
87             sub render_form_errors {
88 6     6 0 8 my ( $self, $rargs ) = @_;
89 6         8 my $out = '';
90 6 50       6 if ( scalar @{$rargs->{form_errors}} ) {
  6         18  
91 0         0 $out .= q{<div class="form_errors>};
92 0         0 foreach my $error ( @{$rargs->{form_errors}} ) {
  0         0  
93 0         0 $out .= qq{<span>$error</span>};
94             }
95 0         0 $out .= q{</div>};
96             }
97 6         12 return $out;
98             }
99              
100             #==============================
101             # Fields
102             #==============================
103              
104             sub render_field {
105 57     57 0 2311 my ( $self, $rargs ) = @_;
106              
107 57         102 $rargs->{rendering} = 'field';
108 57         124 $self->render_hook($rargs);
109              
110 57         85 my $layout_type = $rargs->{layout_type};
111              
112 57         55 my $out;
113 57 100       352 if ( $layout_type eq 'checkbox' ) {
    100          
    100          
    100          
    100          
    100          
114 8         24 $out = $self->render_layout_checkbox($rargs);
115             }
116             elsif ( $layout_type eq 'checkboxgroup' ) {
117 1         4 $out = $self->render_layout_checkboxgroup($rargs);
118             }
119             elsif ( $layout_type eq 'radiogroup' ) {
120 1         5 $out = $self->render_layout_radiogroup($rargs);
121             }
122             elsif ( $layout_type eq 'element' ) { # submit, reset, hidden
123 7         18 $out = $self->render_element($rargs);
124             }
125             elsif ( $layout_type eq 'list' ) { # list
126 1         4 $out = $self->render_layout_list($rargs);
127             }
128             elsif ( $layout_type eq 'display' ) {
129 1         4 $out = $self->render_layout_display($rargs);
130             }
131             else { # $layout_type eq 'standard'
132 38         84 $out = $self->render_layout_standard($rargs);
133             }
134              
135 57         124 return $self->wrap_field($rargs, $out);
136             }
137              
138             sub wrap_field {
139 68     68 0 80 my ( $self, $rargs, $rendered ) = @_;
140              
141             # wrap the field
142 68   66     243 my $wrapper = $rargs->{wrapper} || $self->field_wrapper;
143 68 100       162 return $rendered if $wrapper eq 'none';
144 59   50     216 my $wrapper_meth = $self->can("wrapper_$wrapper") || die "wrapper method '$wrapper' not found";
145 59         122 my $out = $wrapper_meth->($self, $rargs, $rendered);
146 59         291 return $out;
147             }
148              
149             sub render_compound {
150 7     7 0 1108 my ( $self, $rargs, $fields ) = @_;
151              
152 7         9 my $out = '';
153 7 100       20 if ( $rargs->{is_instance} ) {
154 4         8 add_to_class($rargs, 'wrapper_attr', 'repinst');
155             }
156 7         21 $out .= $self->render_label($rargs);
157 7         15 foreach my $field ( @$fields ) {
158 18         68 $out .= $field->render;
159             }
160 7         18 $out = $self->wrap_field($rargs, $out);
161 7         52 return $out;
162             }
163              
164             sub render_repeatable {
165 4     4 0 114 my ( $self, $rargs, $fields ) = @_;
166 4         6 my $out = '';
167 4         15 $out .= $self->render_label($rargs);
168              
169 4         10 foreach my $field ( @$fields ) {
170 8         32 $out .= $field->render;
171             }
172 4         10 $out = $self->wrap_field($rargs, $out);
173 4         26 return $out;
174             }
175              
176             #==============================
177             # Utility methods
178             #==============================
179              
180              
181             sub add_to_class {
182 10     10 1 13 my ( $href, $attr_key, $class ) = @_;
183              
184 10 50       22 return unless defined $class;
185 10 100 66     36 if ( exists $href->{$attr_key}{class} && ref $href->{$attr_key}{class} ne 'ARRAY' ) {
186 2         7 my @classes = split(' ', $href->{$attr_key}{class});
187 2         7 $href->{$attr_key}{class} = \@classes;
188             }
189 10 50 33     43 if ( $class && ref $class eq 'ARRAY' ) {
190 0         0 push @{$href->{$attr_key}{class}}, @$class;
  0         0  
191             }
192             else {
193 10         9 push @{$href->{$attr_key}{class}}, $class;
  10         36  
194             }
195             }
196              
197              
198             sub process_attrs {
199 211     211 1 238 my ($attrs, $skip) = @_;
200              
201 211   100     492 $skip ||= [];
202 211         157 my @use_attrs;
203             my %skip;
204 211         250 @skip{@$skip} = ();
205 211         475 for my $attr( sort keys %$attrs ) {
206 123 100       194 next if exists $skip{$attr};
207 82         74 my $value = '';
208 82 50       162 if( defined $attrs->{$attr} ) {
209 82 100       126 if( ref $attrs->{$attr} eq 'ARRAY' ) {
210             # we don't want class="" if no classes specified
211 21 50       20 next unless scalar @{$attrs->{$attr}};
  21         49  
212 21         23 $value = join (' ', @{$attrs->{$attr}} );
  21         37  
213             }
214             else {
215 61         65 $value = $attrs->{$attr};
216             }
217             }
218 82         252 push @use_attrs, sprintf( '%s="%s"', $attr, $value );
219             }
220 211         280 my $out = join( ' ', @use_attrs );
221 211         401 return $out;
222             }
223              
224             #==============================
225             # Field form elements
226             #==============================
227              
228              
229             sub render_input {
230 49     49 1 53 my ( $self, $rargs ) = @_;
231              
232 49         82 my $input_type = $rargs->{input_type};
233             # checkboxes are special
234 49 100       103 return $self->render_checkbox($rargs) if $input_type eq 'checkbox';
235              
236 46         56 my $name = $rargs->{name};
237 46         49 my $id = $rargs->{id};
238 46         82 my $fif = html_filter($rargs->{fif});
239              
240 46         68 my $out = qq{\n<input type="$input_type" };
241 46         74 $out .= qq{name="$name" };
242 46         63 $out .= qq{id="$id" };
243 46         67 $out .= qq{value="$fif" };
244 46 100       43 add_to_class( $rargs, 'element_attr', 'error' ) if @{$rargs->{errors}};
  46         114  
245 46         104 $out .= process_attrs($rargs->{element_attr});
246 46         70 $out .= "/>";
247 46         62 return $out;
248             }
249              
250              
251             sub render_select {
252 5     5 1 9 my ( $self, $rargs ) = @_;
253              
254 5         7 my $id = $rargs->{id};
255 5         12 my $name = $rargs->{name};
256 5         7 my $size = $rargs->{size};
257              
258             # beginning of select
259 5         7 my $out = qq{\n<select };
260 5         9 $out .= qq{name="$name" };
261 5         10 $out .= qq{id="$id" };
262 5 100       14 $out .= qq{multiple="multiple" } if $rargs->{multiple};
263 5 50       10 $out .= qq{size="$size" } if $size;
264 5 50       7 add_to_class( $rargs, 'element_attr', 'error' ) if @{$rargs->{errors}};
  5         14  
265 5         12 $out .= process_attrs($rargs->{element_attr});
266 5         7 $out .= ">";
267              
268             # render empty_select
269 5 100       17 if ( exists $rargs->{empty_select} ) {
270 3         7 my $label = $self->localize($rargs->{empty_select});
271 3         9 $out .= qq{\n<option value="">$label</option>};
272             }
273              
274             # render options
275 5         8 my $options = $rargs->{options};
276 5         11 foreach my $option ( @$options ) {
277 14 100       27 if ( my $label = $option->{group} ) {
278 3         5 $label = $self->localize( $label );
279 3         4 $out .= qq{\n<optgroup label="$label">};
280 3         2 foreach my $group_opt ( @{ $option->{options} } ) {
  3         5  
281 9         12 $out .= $self->render_select_option($rargs, $group_opt);
282             }
283 3         4 $out .= qq{\n</optgroup>};
284             }
285             else {
286 11         20 $out .= $self->render_select_option($rargs, $option);
287             }
288             }
289              
290             # end of select
291 5         20 $out .= "\n</select>\n";
292 5         8 return $out;
293             }
294              
295             sub render_select_option {
296 20     20 0 23 my ( $self, $rargs, $option ) = @_;
297              
298             # prepare for selected attribute
299 20         18 my $value = $option->{value};
300 20         21 my $multiple = $rargs->{multiple};
301 20   100     41 my $fif = $rargs->{fif} || [];
302 20         17 my %fif_lookup;
303 20 100       33 @fif_lookup{@$fif} = () if $multiple;
304              
305 20         26 my $label = $self->localize($option->{label});
306 20         16 my $out = '';
307 20         21 $out .= qq{\n<option };
308 20         37 $out .= process_attrs($option, ['label', 'order']);
309 20 100 100     115 if ( defined $fif && ( ($multiple && exists $fif_lookup{$value}) || ( $fif eq $value ) ) ) {
      33        
310 3         6 $out .= q{selected="selected" };
311             }
312 20         25 $out .= qq{>$label</option>};
313 20         49 return $out;
314             }
315              
316              
317              
318             sub render_textarea {
319 3     3 1 6 my ( $self, $rargs ) = @_;
320              
321 3         6 my $name = $rargs->{name};
322 3         5 my $id = $rargs->{id};
323 3         9 my $fif = html_filter($rargs->{fif});
324              
325 3         4 my $out = "\n<textarea ";
326 3         6 $out .= qq{name="$name" };
327 3         6 $out .= qq{id="$id" };
328 3 50       3 add_to_class( $rargs, 'element_attr', 'error' ) if @{$rargs->{errors}};
  3         9  
329 3         8 $out .= process_attrs($rargs->{element_attr});
330 3         8 $out .= ">$fif</textarea>";
331 3         5 return $out;
332             }
333              
334              
335             sub render_element {
336 54     54 1 729 my ( $self, $rargs ) = @_;
337              
338 54         71 my $do_errors = delete $rargs->{do_errors};
339 54         65 $rargs->{rendering} = 'element';
340 54         78 $self->render_hook($rargs);
341 54         73 my $form_element = $rargs->{form_element};
342 54         94 my $meth = "render_$form_element";
343 54         129 my $out = $self->$meth($rargs);
344             # this enables doing field.render_element without having to
345             # render the errors for each field.
346 54 100 66     247 if ( $self->render_element_errors && $do_errors ) {
347 9         22 $out .= $self->render_errors($rargs);
348             }
349 54         156 return $out;
350             }
351              
352              
353             sub render_label {
354 60     60 1 78 my ( $self, $rargs, $left_of_label, $right_of_label ) = @_;
355              
356 60 100       139 return '' if $rargs->{no_label};
357 55 100       108 return '' if $rargs->{is_contains};
358 51 100 100     170 return '' if $rargs->{wrapper} && $rargs->{wrapper} eq 'fieldset'; # this is kludgy :(
359 47         58 $rargs->{rendering} = 'label';
360 47         80 $self->render_hook($rargs);
361 47   100     213 $right_of_label ||= '';
362 47   100     131 $left_of_label ||= '';
363              
364 47         60 my $id = $rargs->{id};
365 47   33     179 my $label = $rargs->{display_label} || $self->localize($rargs->{label});
366 47         59 my $out = qq{\n<label };
367 47         97 $out .= qq{for="$id"};
368 47         264 $out .= process_attrs($rargs->{label_attr});
369 47         97 $out .= qq{>};
370 47         69 $out .= qq{$left_of_label$label$right_of_label};
371 47         48 $out .= qq{</label>};
372 47         92 return $out
373             }
374              
375              
376             sub render_errors {
377 58     58 1 219 my ( $self, $rargs ) = @_;
378              
379 58         74 $rargs->{rendering} = 'errors';
380 58         87 $self->render_hook($rargs);
381 58   50     158 my $errors = $rargs->{errors} || [];
382 58         59 my $out = '';
383 58   33     267 my $error_tag = $rargs->{error_tag} || $self->error_tag;
384 58   33     224 my $error_class = $rargs->{error_class} || $self->error_class;
385 58         107 foreach my $error (@$errors) {
386 7         25 $out .= qq{\n<$error_tag class="$error_class">$error</$error_tag>};
387             }
388 58         100 return $out;
389             }
390              
391             sub html_filter {
392 60     60 0 59 my $string = shift;
393 60 50       121 return '' if (!defined $string);
394 60         76 $string =~ s/&/&amp;/g;
395 60         58 $string =~ s/</&lt;/g;
396 60         62 $string =~ s/>/&gt;/g;
397 60         53 $string =~ s/"/&quot;/g;
398 60         84 return $string;
399             }
400              
401             sub render_option {
402 0     0 0 0 my ( $self, $rargs, $option ) = @_;
403 0         0 my $out = '';
404 0 0       0 if ( $rargs->{layout_type} eq 'standard' ) {
    0          
    0          
405 0         0 $out .= $self->render_select_option($rargs, $option);
406             }
407             elsif ( $rargs->{layout_type} eq 'checkboxgroup' ) {
408 0         0 $out .= $self->render_checkbox_option($rargs, $option);
409             }
410             elsif ( $rargs->{layout_type} eq 'radiogroup' ) {
411 0         0 $out .= $self->render_radio_option($rargs, $option);
412             }
413 0         0 return $out;
414             }
415              
416             #==============================
417             # Radio, Radiogroup
418             #==============================
419              
420             sub render_layout_radiogroup {
421 1     1 0 1 my ( $self, $rargs ) = @_;
422              
423 1   33     10 my $rdgo_layout = $rargs->{rdgo_layout} || $self->rdgo_layout;
424 1 50       6 my $rdgo_layout_meth = $self->can("rdgo_layout_$rdgo_layout") or die "Radio layout '$rdgo_layout' not found";
425              
426 1         4 my $out = $self->render_label($rargs);
427             # render options
428 1         1 my $options = $rargs->{options};
429 1         2 foreach my $option ( @$options ) {
430 2         3 $out .= $rdgo_layout_meth->($self, $rargs, $option);
431             }
432             # TODO - is this the best place for error messages for radiogroups?
433 1         4 $out .= $self->render_errors($rargs);
434 1         2 return $out;
435             }
436              
437             sub render_radio_option {
438 2     2 0 3 my ( $self, $rargs, $option ) = @_;
439              
440 2         2 my $name = $rargs->{name};
441 2         1 my $order = $option->{order};
442 2         2 my $out = qq{<input type="radio" };
443 2         3 $out .= qq{name="$name" };
444 2 50       5 $out .= qq{id="$name$order" } unless $option->{id};
445 2         3 $out .= process_attrs($option, ['label', 'order']);
446 2 50       6 if ( $rargs->{fif} eq $option->{value} ) {
447 0         0 $out .= qq{checked="checked" };
448             }
449 2         4 $out .= q{/>};
450             }
451              
452             sub rdgo_layout_labels_left {
453 0     0 0 0 my ( $self, $rargs, $option ) = @_;
454 0         0 my $rd_element = $self->render_radio_option($rargs, $option);
455 0         0 my $rd = $self->render_radio_label($rargs, $option, '', $rd_element);
456 0         0 my $out = $self->element_wrapper($rargs, $rd);
457 0         0 return $out;
458             }
459              
460             sub rdgo_layout_labels_right {
461 2     2 0 2 my ( $self, $rargs, $option ) = @_;
462 2         4 my $rd_element = $self->render_radio_option($rargs, $option);
463 2         4 my $rd = $self->render_radio_label($rargs, $option, $rd_element, '');
464 2         4 my $out = $self->element_wrapper($rargs, $rd);
465 2         4 return $out;
466             }
467              
468             sub render_radio_label {
469 2     2 0 4 my ( $self, $rargs, $option, $left_of_label, $right_of_label ) = @_;
470              
471 2   50     8 $right_of_label ||= '';
472 2   50     3 $left_of_label ||= '';
473 2         4 my $label = $self->localize($option->{label});
474              
475 2         4 my $attrs = { class => ['radio'] };
476 2 50       5 $attrs->{for} = $option->{id} ? $option->{id} : $rargs->{name} . $option->{order};
477             # add_to_class( $attrs, $rargs->{radio_label_class} );
478              
479 2         3 my $out = qq{\n<label };
480 2         3 $out.= process_attrs($attrs);
481 2         3 $out .= q{>};
482 2         3 $out .= qq{$left_of_label $label $right_of_label};
483 2         5 $out .= qq{</label>};
484             }
485              
486             #==============================
487             # Checkboxes
488             #==============================
489              
490             sub render_layout_checkbox {
491 8     8 0 12 my ( $self, $rargs) = @_;
492              
493 8         17 my $cb_element = $self->render_checkbox($rargs);
494 8   66     32 my $cb_layout = $rargs->{cb_layout} || $self->cb_layout;
495 8   50     320 my $meth = $self->can("cb_layout_$cb_layout") || die "Checkbox layout '$cb_layout' not found";
496 8         11 my $out = '';
497 8         24 $out = $meth->($self, $rargs, $cb_element);
498 8         21 $out .= $self->render_errors($rargs);
499              
500 8         18 return $out;
501              
502             }
503              
504              
505             sub render_checkbox {
506 11     11 1 14 my ( $self, $rargs ) = @_;
507              
508 11         14 my $name = $rargs->{name};
509 11         12 my $id = $rargs->{name};
510 11         15 my $checkbox_value = $rargs->{checkbox_value};
511 11         24 my $fif = html_filter($rargs->{fif});
512              
513 11         14 my $out = qq{<input };
514 11         18 $out .= qq{type="checkbox" };
515 11         19 $out .= qq{name="$name" };
516 11         17 $out .= qq{id="$id" };
517 11         16 $out .= qq{value="$checkbox_value" };
518 11 100       27 $out .= qq{checked="checked" } if $fif eq $checkbox_value;
519 11 50       9 add_to_class( $rargs, 'element_attr', 'error' ) if @{$rargs->{errors}};
  11         27  
520 11         30 $out .= process_attrs($rargs->{element_attr});
521 11         23 $out .= "/>";
522 11         17 return $out;
523             }
524              
525             sub render_checkbox_option {
526 3     3 0 4 my ( $self, $rargs, $option ) = @_;
527              
528             # prepare for checked attribute
529 3         4 my $multiple = $rargs->{multiple};
530 3   50     9 my $fif = $rargs->{fif} || [];
531 3         3 my %fif_lookup;
532 3 50       5 @fif_lookup{@$fif} = () if $multiple;
533              
534              
535 3         3 my $name = $rargs->{name};
536 3         3 my $value = $option->{value};
537 3         3 my $order = $option->{order};
538 3         3 my $out = qq{<input };
539 3         4 $out .= qq{type="checkbox" };
540 3         4 $out .= qq{name="$name" };
541 3 50       12 $out .= qq{id="$name$order" } unless $option->{id};
542 3 50 33     21 if ( defined $fif && ( ($multiple && exists $fif_lookup{$value}) || ( $fif eq $value ) ) ) {
      33        
543 0         0 $out .= q{checked="checked" };
544             }
545 3         7 $out .= process_attrs($option, ['label', 'order']);
546 3         4 $out .= "/>";
547 3         4 return $out;
548             }
549              
550             sub render_layout_checkboxgroup {
551 1     1 0 1 my ( $self, $rargs ) = @_;
552              
553 1         5 my $out = $self->render_label($rargs);
554 1   33     8 my $cbgo_layout = $rargs->{cbgo_layout} || $self->cbgo_layout;
555 1   50     7 my $cbgo_layout_meth = $self->can("cbgo_layout_$cbgo_layout")
556             || die "Checkbox group option layout '$cbgo_layout' not found";;
557             # render options
558 1         2 my $options = $rargs->{options};
559 1         2 foreach my $option ( @$options ) {
560 3         6 $out .= $cbgo_layout_meth->($self, $rargs, $option);
561             }
562             # TODO - is this the best place for error messages?
563 1         4 $out .= $self->render_errors($rargs);
564 1         2 return $out;
565             }
566              
567             sub cbgo_layout_labels_right {
568 3     3 0 3 my ( $self, $rargs, $option ) = @_;
569              
570 3         5 my $cb_element = $self->render_checkbox_option($rargs, $option);
571 3         7 my $cb = $self->render_checkbox_label($rargs, $option, $cb_element, '');
572 3         8 my $out .= $self->element_wrapper($rargs, $cb);
573 3         8 return $out;
574             }
575              
576             sub cbgo_layout_labels_left {
577 0     0 0 0 my ( $self, $rargs, $option ) = @_;
578 0         0 my $cb_element = $self->render_checkbox_option($rargs, $option);
579 0         0 my $cb = $self->render_checkbox_label($rargs, $option, '', $cb_element);
580 0         0 my $out .= $self->element_wrapper($rargs, $cb);
581 0         0 return $out;
582             }
583              
584             sub render_checkbox_label {
585 3     3 0 4 my ( $self, $rargs, $option, $left_of_label, $right_of_label ) = @_;
586              
587 3   50     10 $right_of_label ||= '';
588 3   50     4 $left_of_label ||= '';
589 3         5 my $label = $self->localize($option->{label});
590              
591 3         7 my $attrs = { class => ['checkbox'] };
592 3 50       13 $attrs->{for} = $option->{id} ? $option->{id} : $rargs->{name} . $option->{order};
593             # add_to_class( $attrs, $rargs->{checkbox_label_class} );
594              
595 3         2 my $out = qq{\n<label };
596 3         4 $out.= process_attrs($attrs);
597 3         3 $out .= q{>};
598 3         5 $out .= qq{$left_of_label $label $right_of_label};
599 3         7 $out .= qq{</label>};
600             }
601              
602              
603             sub cb_layout_cbwrll {
604 4     4 0 8 my ( $self, $rargs, $cb_element ) = @_;
605              
606 4         11 my $out = $self->render_label($rargs, '', $cb_element);
607 4         10 return $out
608              
609             }
610              
611              
612             sub cb_layout_cbwrlr {
613 2     2 0 3 my ( $self, $rargs, $cb_element ) = @_;
614              
615 2         7 my $out = $self->render_label($rargs, $cb_element, '' );
616 2         3 return $out;
617             }
618              
619              
620             sub cb_layout_cbnowrll {
621 1     1 0 2 my ( $self, $rargs, $cb_element ) = @_;
622              
623 1         6 my $out = $self->render_label($rargs);
624 1         2 $out .= $cb_element;
625 1         3 return $out;
626             }
627              
628             sub cb_layout_cb2l {
629 1     1 0 2 my ( $self, $rargs, $cb_element ) = @_;
630              
631 1         3 my $out = $self->render_label($rargs);
632              
633 1         3 my $id = $rargs->{id};
634 1   50     4 my $option_label = $self->localize($rargs->{option_label}) || '';
635 1         7 $out .= qq{\n<label for="$id">$cb_element$option_label</label>};
636 1         4 return $out;
637             }
638              
639             #==============================
640             # Layouts
641             #==============================
642              
643             sub render_layout_standard {
644 38     38 0 42 my ( $self, $rargs ) = @_;
645              
646             # render the field layout
647 38   66     185 my $layout = $rargs->{layout} || $self->standard_layout;
648 38         137 my $layout_meth = $self->can("layout_$layout");
649 38 50       76 die "layout method '$layout' not found" unless $layout_meth;
650 38         42 my $out = '';
651 38         72 $out .= $layout_meth->($self, $rargs);
652 38         60 return $out;
653             }
654              
655             sub layout_lbl_ele_err {
656 36     36 0 36 my ( $self, $rargs ) = @_;
657              
658 36         39 my $out = '';
659 36         74 $out .= $self->render_label($rargs);
660 36         81 $out .= $self->render_element($rargs);
661 36         71 $out .= $self->render_errors($rargs);
662 36         60 return $out;
663             }
664              
665             sub layout_lbl_wrele_err {
666 1     1 0 2 my ( $self, $rargs ) = @_;
667              
668 1         1 my $out = '';
669 1         3 $out .= $self->render_label($rargs);
670 1         3 my $ele .= $self->render_element($rargs);
671 1         12 $out .= $self->element_wrapper($rargs, $ele);
672 1         3 $out .= $self->render_errors($rargs);
673 1         7 return $out;
674             }
675              
676             sub layout_no_label {
677 1     1 0 2 my ( $self, $rargs ) = @_;
678 1         2 my $out = '';
679 1         3 $out .= $self->render_element($rargs);
680 1         4 $out .= $self->render_errors($rargs);
681 1         3 return $out;
682             }
683              
684             #==============================
685             # Wrappers
686             #==============================
687              
688             sub wrapper_simple {
689 55     55 0 64 my ( $self, $rargs, $rendered ) = @_;
690              
691 55   33     268 my $tag = $rargs->{wrapper_attr}{tag} || $self->wrapper_tag;
692 55         82 my $out = qq{\n<$tag };
693 55         131 $out .= process_attrs($rargs->{wrapper_attr}, ['tag']);
694 55         86 $out .= qq{>};
695 55         87 $out .= $rendered;
696 55         83 $out .= qq{\n</$tag>};
697 55         81 return $out;
698             }
699              
700             sub wrapper_fieldset {
701 4     4 0 8 my ( $self, $rargs, $rendered ) = @_;
702              
703 4 100       12 my $id = $rargs->{id} if ($rargs->{is_compound});
704 4         11 my $label = $self->localize($rargs->{label});
705 4         5 my $out = qq{\n<fieldset };
706 4 100       12 $out .= qq{id="$id" } if $id;
707 4         8 $out .= process_attrs($rargs->{wrapper_attr});
708 4         6 $out .= qq{>};
709 4         7 $out .= qq{<legend class="label">$label</legend>};
710 4         7 $out .= $rendered;
711 4         4 $out .= qq{\n</fieldset>};
712 4         15 return $out;
713             }
714              
715             sub element_wrapper {
716 9     9 0 11 my ( $self, $rargs, $rendered ) = @_;
717 9         14 my $out = qq{\n<div };
718 9         19 $out .= process_attrs($rargs->{element_wrapper_attr});
719 9         21 $out .= qq{>$rendered</div>};
720 9         17 return $out;
721             }
722              
723             sub render_layout_list {
724 1     1 0 3 my ( $self, $rargs ) = @_;
725              
726 1   50     11 my $fif = $rargs->{fif} || [];
727 1         2 my $size = $rargs->{size};
728 1   50     4 $size ||= (scalar @{$fif} || 0) + ($rargs->{num_extra} || 0);
      50        
      33        
729 1   50     2 $size ||= 2;
730 1         4 my $out = $self->render_label($rargs);
731 1         1 my $index = 0;
732 1         3 while ( $size ) {
733 3         3 my $value = shift @$fif;
734 3 50       4 $value = defined $value ? $value : '';
735 3         25 my $element = $self->render_input({%$rargs, fif => $value, id => $rargs->{id} . $index++ });
736 3         12 $out .= $self->element_wrapper($rargs, $element);
737 3         7 $size--;
738             }
739 1         2 return $out;
740             }
741              
742             sub render_layout_display {
743 1     1 0 2 my ( $self, $rargs ) = @_;
744              
745             # render the field layout
746 1   33     4 my $layout = $rargs->{layout} || $self->display_layout;
747 1         5 my $layout_meth = $self->can("layout_$layout");
748 1 50       3 die "layout method '$layout' not found" unless $layout_meth;
749 1         2 my $out = '';
750 1         2 $out .= $layout_meth->($self, $rargs);
751 1         4 return $out;
752             }
753              
754             sub layout_span {
755 1     1 0 2 my ( $self, $rargs ) = @_;
756              
757 1         1 my $out= '<span';
758 1         3 $out .= ' id="' . $rargs->{id} . '"';
759 1         4 $out .= process_attrs($rargs->{element_attr});
760 1         2 $out .= '>';
761 1         2 $out .= $rargs->{fif};
762 1         1 $out .= '</span>';
763 1         2 return $out;
764             }
765              
766             1;
767              
768             __END__
769              
770             =pod
771              
772             =encoding UTF-8
773              
774             =head1 NAME
775              
776             Data::MuForm::Renderer::Base - Renderer
777              
778             =head1 VERSION
779              
780             version 0.03
781              
782             =head1 DESCRIPTION
783              
784             Base functionality for renderers, including rendering standard form controls.
785              
786             You should normally create your own custom renderer class which inherits
787             from this one. In it you should set the various standard defaults for your
788             rendering, and override some of the methods, like 'render_errors'. You should
789             also create custom layouts using the layout sub naming convention so they can
790             be found.
791              
792             There is a 'render_hook' which can be used to customize things like classes and
793             attributes. It is called on every 'render_field', 'render_element', 'render_errors',
794             and 'render_label' call. The hook can be used in the renderer or in the form class,
795             whichever is most appropriate.
796              
797             This is Perl code, and can be customized however you want. This base renderer is
798             supplied as a library of useful routines. You could replace it entirely if you want,
799             as long you implement the methods that are used in the form and field classes.
800              
801             As part of an attempt to separate the core code more from the rendering code, and
802             limit the explosion of various rendering attributes, the
803             rendering is always done using a 'render_args' hashref of the pieces of the form
804             and field that are needed for rendering. Most of the rendering settings are set
805             as keys in the render_args hashref, with some exceptions. This means that you
806             can just start using a new render_args hashref key in your custom rendering code
807             without having to do anything special to get it there. You have to set it somewhere
808             of course, either in the field definition or passed in on the rendering calls.
809              
810             For a particular field, the field class will supply a 'base_render_args', which is
811             merged with the 'render_args' from the field definition, which is merged with
812             the 'render_args' from the actual rendering call.
813              
814             One of the main goals of this particular rendering iteration has been to make
815             it easy and seamless to limit rendering to only the field elements, so that all
816             of the complicated divs and classes that are necessary for recent 'responsive'
817             CSS frameworks can be done in the templates under the control of the frontend
818             programmers.
819              
820             [% form.field('foo').render_element({ class => 'mb10 tye', placeholder => 'Type...'}) %]
821              
822             Or render the element, the errors and the labels, and do all of the other formatting
823             in the template:
824              
825             [% field = form.field('foo') %]
826             <div class="sm tx10">
827             [% field.render_label({ class="cxx" }) %]
828             <div class="xxx">
829             [% field.render_element({ class => 'mb10 tye', placeholder => 'Type...'}) %]
830             </div>
831             <div class="field-errors">[% field.render_errors %]</div>
832             </div>
833              
834             And yet another goal has been to make it possible to render a form automatically
835             and have it just work.
836              
837             [% form.render %]
838              
839             =head2 Renderer defaults
840              
841             =over 4
842              
843             =item standard_layout
844              
845             For normal inputs and select fields.
846              
847             Provided:
848              
849             lbl_ele_err - label, element, error
850             lbl_wrele_err - label, wrapped element, error
851              
852             Create new layouts starting with 'layout_<name>'.
853              
854             =item cb_layout
855              
856             Default checkbox layout. Create new checkbox layouts with methods starting
857             with 'cb_layout_<name>'.
858              
859             Provided:
860              
861             cbwrll - checkbox, wrapped, label left
862             cbwrlr - checkbox, wrapped, label right
863             cbnowrll - checkbox, not wrapped, label left
864             cb2l - checkbox, 2 labels
865              
866             =item rdgo_layout
867              
868             Default radiogroup option layout
869              
870             Provided:
871              
872             labels_left
873             labels_right
874              
875             Supply more with 'rdgo_layout_<name>'.
876              
877             =item cbgo_layout
878              
879             Default checkbox group option layout
880              
881             Provided:
882              
883             labels_left
884             labels_right
885              
886             Supply more with 'cbgo_layout_<name>'.
887              
888             =item display_layout
889              
890             Default 'display' field layout.
891              
892             Provided:
893              
894             span
895              
896             Provide more options with 'layout_<name>'.
897              
898             =item field_wrapper
899              
900             Default field wrapper. Supply more with 'wrapper_<name>'.
901              
902             Provided:
903              
904             simple
905             fieldset
906              
907             =item wrapper_tag
908              
909             Default wrapper tag. Default: 'div'.
910              
911             =item error_tag
912              
913             Default error tag.
914              
915             =back
916              
917             =head1 NAME
918              
919             Data::MuForm::Renderer::Base
920              
921             =head1 Layouts
922              
923             The 'standard' layouts are for all fields that don't have another layout type.
924             This includes text fields, selects, and textareas. Create a new layout with 'layout_<layout-name'.
925             Included layouts are 'lbl_ele_err' and 'no_label'. The default is 'lbl_ele_err', which renders
926             a label, the form control, and then error messages.
927              
928             The 'radiogroup' layouts are for radiogroups, which is a 'Select' field layout type.
929             Create a new layout with 'rd_layout_<layout name>'.
930             Radiogroup option layouts are named 'rdgo_layout_<layout name>'. The provided layouts
931             are 'right_label' and 'left_label'.
932              
933             Checkbox layout methods are named 'cb_layout_<layout name>'. The provided layouts are
934             'cbwrll' - checkbox wrapped, label left, 'cbwrlr' - checkbox wrapped, label right,
935             'cb2l' - checkbox with two labels, 'cbnowrll' - checkbox unwrapped, label left.
936              
937             The checkbox group layouts are another 'Select' field layout type.
938             Checkbox group options layouts are named 'cbgo_layout_<layout name>'.
939              
940             =head1 Form and Field methods
941              
942             In the field:
943              
944             render (render_field, render_compound or render_repeatable in the renderer)
945             render_element (for standard single elements)
946             render_label
947             render_errors
948              
949             In the Select field:
950              
951             render_option (for select, radio group and checkbox group)
952              
953             In the form:
954              
955             render (render_form in the renderer)
956             render_start
957             render_end
958             render_errors (render_form_errors in the renderer)
959              
960             =head2 add_to_class
961              
962             Utility class used to add to the 'class' key of an attribute hashref,
963             handling arrayref/not-arrayref, etc. Used to add 'error' and 'required'
964             classes.
965              
966             =head2 process_attrs
967              
968             Takes a hashref of key-value pairs to be rendered into HTML attributes.
969             Second param ($skip) is keys to skip in the hashref.
970              
971             All 'values' are html filtered.
972              
973             =head2 render_input
974              
975             =head2 render_select
976              
977             =head2 render_textarea
978              
979             =head2 render_element
980              
981             =head2 render_label
982              
983             =head2 render_errors
984              
985             =head2 render_checkbox
986              
987             =head2 cbwrll
988              
989             Checkbox, wrapped, label left
990              
991             <label class="checkbox" for="option1"><input id="option1" name="option1" type="checkbox" value="1" /> Option1 </label>
992              
993             =head2 cbwrlr
994              
995             Checkbox wrapped, label right
996              
997             =head2 cbnowrll
998              
999             Checkbox not wrapped, label left
1000              
1001             =head1 AUTHOR
1002              
1003             Gerda Shank
1004              
1005             =head1 COPYRIGHT AND LICENSE
1006              
1007             This software is copyright (c) 2017 by Gerda Shank.
1008              
1009             This is free software; you can redistribute it and/or modify it under
1010             the same terms as the Perl 5 programming language system itself.
1011              
1012             =cut