@@ -1073,10 +1073,10 @@ section above) is to add:
1073
1073
Using Actions to Change your Form: CollectionType
1074
1074
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1075
1075
1076
- Have you ever used Symfony's `CollectionType `_? If you want to be able
1077
- to dynamically add or remove embedded forms, you need to write some
1078
- JavaScript. Well, that's true, unless you render your form inside a
1079
- live component .
1076
+ Symfony's `CollectionType `_ can be used to embed a collection of
1077
+ embedded forms including allowing the user to dynamically add or remove
1078
+ them. Live components can accomplish make this all possible while
1079
+ writing zero JavaScript .
1080
1080
1081
1081
For example, imagine a "Blog Post" form with an embedded "Comment" forms
1082
1082
via the ``CollectionType ``::
@@ -1100,6 +1100,7 @@ via the ``CollectionType``::
1100
1100
'entry_type' => CommentFormType::class,
1101
1101
'allow_add' => true,
1102
1102
'allow_delete' => true,
1103
+ 'by_reference' => false,
1103
1104
])
1104
1105
;
1105
1106
}
@@ -1144,14 +1145,76 @@ Now, create a Twig component to render the form::
1144
1145
// "formValues" represents the current data in the form
1145
1146
// this modifies the form to add an extra comment
1146
1147
// the result: another embedded comment form!
1148
+ // change "comments" to the name of the field that uses CollectionType
1147
1149
$this->formValues['comments'][] = [];
1148
1150
}
1151
+
1152
+ #[LiveAction]
1153
+ public function removeComment(#[LiveArg] int $index)
1154
+ {
1155
+ unset($this->formValues['comments'][$index]);
1156
+ }
1149
1157
}
1150
1158
1151
- Finally, render the form in the component's template like normal, but
1152
- with a live action that points to ``addComment() ``:
1159
+ The template for this component has two jobs: (1) render the form
1160
+ like normal and (2) include links that trigger the ``addComment() ``
1161
+ and ``removeComment() `` actions:
1162
+
1163
+ .. code-block :: twig
1164
+
1165
+ <div{{ attributes }}>
1166
+ {{ form_start(this.form) }}
1167
+ {{ form_row(this.form.title) }}
1168
+
1169
+ <h3>Comments:</h3>
1170
+ {% for key, commentForm in this.form.comments %}
1171
+ <button
1172
+ data-action="live#action"
1173
+ data-action-name="removeComment(index={{ key }})"
1174
+ type="button"
1175
+ >X</button>
1176
+
1177
+ {{ form_widget(commentForm) }}
1178
+ {% endfor %}
1179
+ </div>
1180
+
1181
+ {# avoid an extra label for this field #}
1182
+ {% do this.form.comments.setRendered %}
1183
+
1184
+ <button
1185
+ data-action="live#action"
1186
+ data-action-name="addComment"
1187
+ type="button"
1188
+ >+ Add Comment</button>
1189
+
1190
+ <button type="submit" >Save</button>
1191
+ {{ form_end(this.form) }}
1192
+ </div>
1193
+
1194
+ Done! Behind the scenes, it works like this:
1195
+
1196
+ A) When the user clicks "+ Add Comment", an Ajax request is sent that
1197
+ triggers the ``addComment() `` action.
1198
+
1199
+ B) ``addComment() `` modifies ``formValues ``, which you can think of as
1200
+ the raw "POST" data of your form.
1201
+
1202
+ C) Still during the Ajax request, the ``formValues `` are "submitted"
1203
+ into your form. The new key inside of ``$this->formValues['comments'] ``
1204
+ tells the ``CollectionType `` that you want a new, embedded form.
1205
+
1206
+ D) The form is rendered - now with another embedded form! - and the
1207
+ Ajax call returns with the form (with the new embedded form).
1208
+
1209
+ When the user clicks ``removeComment() ``, a similar process happens.
1210
+
1211
+ .. note ::
1153
1212
1154
- TODO
1213
+ When working with Doctrine entities, add ``orphanRemoval: true ``
1214
+ and ``cascade={"persist"} `` to your ``OneToMany `` relationship.
1215
+ In this example, these options would be added to the ``OneToMany ``
1216
+ attribute above the ``Post.comments `` property. These help new
1217
+ items save and deletes any items whose embedded forms are removed.
1155
1218
1156
1219
Modifying Embedded Properties with the "exposed" Option
1157
1220
-------------------------------------------------------
@@ -1636,3 +1699,4 @@ bound to Symfony's BC policy for the moment.
1636
1699
.. _`dependent form fields` : https://symfony.com/doc/current/form/dynamic_form_modification.html#dynamic-generation-for-submitted-forms
1637
1700
.. _`Symfony UX configured in your app` : https://symfony.com/doc/current/frontend/ux.html
1638
1701
.. _`Component attributes` : https://symfony.com/bundles/ux-twig-component/current/index.html#component-attributes
1702
+ .. _`CollectionType` : https://symfony.com/doc/current/form/form_collections.html
0 commit comments