@@ -12,6 +12,7 @@ __PACKAGE__->mk_item_accessors( qw(
12
12
_original_elements
13
13
increment_field_names
14
14
counter_name
15
+ repeatable_delimiter
15
16
) );
16
17
17
18
sub new {
@@ -20,6 +21,10 @@ sub new {
20
21
$self -> filename(' repeatable' );
21
22
$self -> is_repeatable(1);
22
23
$self -> increment_field_names(1);
24
+ # TODO
25
+ # This setter is currently not documentes as FF::Model::HashRef
26
+ # only supports '_'
27
+ $self -> repeatable_delimiter(' _' );
23
28
24
29
return $self ;
25
30
}
@@ -49,7 +54,152 @@ sub repeat {
49
54
$self -> _elements( [] );
50
55
51
56
return [] if !$count ;
52
-
57
+
58
+ # switch behaviour
59
+ # If nested_name is set, we add the repeatable counter to the name
60
+ # of the containing block (this repeatable block).
61
+ # This behaviour eases the creation of client side javascript code
62
+ # to add and remove repeatable elements client side.
63
+ # If nested_name is *not* set, we add the repeatable counter to the names
64
+ # of the child elements (leaves of the element tree).
65
+ my $nested_name = $self -> nested_name;
66
+ if (defined $nested_name && length $nested_name ) {
67
+ return $self -> _repeat_containing_block( $count );
68
+ }
69
+ else {
70
+ return $self -> _repeat_child_elements( $count );
71
+ }
72
+ }
73
+
74
+ sub _repeat_containing_block {
75
+ my ( $self , $count ) = @_ ;
76
+
77
+ my $children = $self -> _original_elements;
78
+
79
+ # We must not get 'nested.nested_1' instead of 'nested_1' through the
80
+ # nested_name attribute of the Repeatable element, thus we extended
81
+ # FF::Elements::_Field nested_names method to ignore Repeatable elements.
82
+ my $nested_name = $self -> nested_name;
83
+ $self -> original_nested_name( $nested_name );
84
+
85
+ # delimiter between nested_name and the incremented counter
86
+ my $delimiter = $self -> repeatable_delimiter;
87
+
88
+ my @return ;
89
+
90
+ for my $rep ( 1 .. $count ) {
91
+ # create clones of elements and put them in a new block
92
+ my @clones = map { $_ -> clone } @$children ;
93
+ my $block = $self -> element(' Block' );
94
+
95
+ # initiate new block with properties of this repeatable
96
+ $block -> _elements( \@clones );
97
+ $block -> attributes( $self -> attributes );
98
+ $block -> tag( $self -> tag );
99
+
100
+ $block -> repeatable_count($rep );
101
+
102
+ if ( $self -> increment_field_names ) {
103
+ # store the original nested_name attribute for later usage when
104
+ # building the original nested name
105
+ $block -> original_nested_name( $block -> nested_name )
106
+ if !defined $block -> original_nested_name;
107
+
108
+ # create new nested name with repeat counter
109
+ $block -> nested_name( $nested_name . $delimiter . $rep );
110
+
111
+ for my $field ( @{ $block -> get_fields } ) {
112
+
113
+ if ( defined ( my $name = $field -> name ) ) {
114
+ # store original name for later usage when
115
+ # replacing the field names in constraints
116
+ $field -> original_name($name )
117
+ if !defined $field -> original_name;
118
+
119
+ # store original nested name for later usage when
120
+ # replacing the field names in constraints
121
+ $field -> original_nested_name( $field -> build_original_nested_name )
122
+ if !defined $field -> original_nested_name;
123
+ }
124
+ }
125
+ }
126
+
127
+ _reparent_children($block );
128
+
129
+ for my $field ( @{ $block -> get_fields } ) {
130
+ map { $_ -> parent($field ) }
131
+ @{ $field -> _deflators },
132
+ @{ $field -> _filters },
133
+ @{ $field -> _constraints },
134
+ @{ $field -> _inflators },
135
+ @{ $field -> _validators },
136
+ @{ $field -> _transformers },
137
+ @{ $field -> _plugins },
138
+ ;
139
+ }
140
+
141
+ my $block_fields = $block -> get_fields;
142
+
143
+ my @block_constraints = map { @{ $_ -> get_constraints } } @$block_fields ;
144
+
145
+ # rename any 'others' fields
146
+ my @others_constraints = grep { defined $_ -> others }
147
+ grep { $_ -> can(' others' ) } @block_constraints ;
148
+
149
+ for my $constraint (@others_constraints ) {
150
+ my $others = $constraint -> others;
151
+ if ( !ref $others ) {
152
+ $others = [$others ];
153
+ }
154
+ my @new_others ;
155
+
156
+ for my $name (@$others ) {
157
+ my $field
158
+ = ( first { $_ -> original_nested_name eq $name }
159
+ @$block_fields )
160
+ || first { $_ -> original_name eq $name } @$block_fields ;
161
+
162
+ if ( defined $field ) {
163
+ push @new_others , $field -> nested_name;
164
+ }
165
+ else {
166
+ push @new_others , $name ;
167
+ }
168
+ }
169
+
170
+ $constraint -> others( \@new_others );
171
+ }
172
+
173
+ # rename any 'when' fields
174
+ my @when_constraints = grep { defined $_ -> when } @block_constraints ;
175
+
176
+ for my $constraint (@when_constraints ) {
177
+ my $when = $constraint -> when;
178
+ my $name = $when -> {field };
179
+
180
+ my $field
181
+ = first { $_ -> original_nested_name eq $name } @$block_fields ;
182
+
183
+ if ( defined $field ) {
184
+ $when -> {field } = $field -> nested_name;
185
+ }
186
+ }
187
+
188
+ push @return , $block ;
189
+
190
+ }
191
+
192
+ return \@return ;
193
+ }
194
+
195
+ sub _repeat_child_elements {
196
+ my ( $self , $count ) = @_ ;
197
+
198
+ my $children = $self -> _original_elements;
199
+
200
+ # delimiter between nested_name and the incremented counter
201
+ my $delimiter = $self -> repeatable_delimiter;
202
+
53
203
my @return ;
54
204
55
205
for my $rep ( 1 .. $count ) {
@@ -72,7 +222,7 @@ sub repeat {
72
222
$field -> original_nested_name( $field -> nested_name )
73
223
if !defined $field -> original_nested_name;
74
224
75
- $field -> name(" ${name} _ $ rep" );
225
+ $field -> name(${name} . $delimiter . $ rep );
76
226
}
77
227
}
78
228
}
@@ -333,6 +483,35 @@ C<n> is the L</repeatable_count> value.
333
483
This is set on each new L<Block|HTML::FormFu::Element::Block> element
334
484
returned by L</repeat> , starting at number C<1 > .
335
485
486
+ Because this is an 'inherited accessor' available on all elements, it can be
487
+ used to determine whether any element is a child of a Repeatable element.
488
+
489
+ =head2 nested_name
490
+
491
+ If the L</nested_name> attribute is set the naming scheme of the Repeatable
492
+ elements children is switched to add the counter to the repeatable blocks
493
+ themself.
494
+
495
+ ---
496
+ elements:
497
+ - type: Repeatable
498
+ nested_name: my_rep
499
+ elements:
500
+ - name: foo
501
+ - name: bar
502
+
503
+ Calling C<< $element->repeat(2) >> would result in the following markup:
504
+
505
+ <div>
506
+ <input name="my_rep_1.foo" type="text" />
507
+ <input name="my_rep_1.bar" type="text" />
508
+ </div>
509
+ <div>
510
+ <input name="myrep_2.foo" type="text" />
511
+ <input name="myrep_2.bar" type="text" />
512
+ </div>
513
+
514
+
336
515
Because this is an 'inherited accessor' available on all elements, it can be
337
516
used to determine whether any element is a child of a Repeatable element.
338
517
0 commit comments