@@ -1073,10 +1073,10 @@ section above) is to add:
10731073 Using Actions to Change your Form: CollectionType
10741074~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
10751075
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 .
10801080
10811081For example, imagine a "Blog Post" form with an embedded "Comment" forms
10821082via the ``CollectionType ``::
@@ -1100,6 +1100,7 @@ via the ``CollectionType``::
11001100 'entry_type' => CommentFormType::class,
11011101 'allow_add' => true,
11021102 'allow_delete' => true,
1103+ 'by_reference' => false,
11031104 ])
11041105 ;
11051106 }
@@ -1144,14 +1145,76 @@ Now, create a Twig component to render the form::
11441145 // "formValues" represents the current data in the form
11451146 // this modifies the form to add an extra comment
11461147 // the result: another embedded comment form!
1148+ // change "comments" to the name of the field that uses CollectionType
11471149 $this->formValues['comments'][] = [];
11481150 }
1151+
1152+ #[LiveAction]
1153+ public function removeComment(#[LiveArg] int $index)
1154+ {
1155+ unset($this->formValues['comments'][$index]);
1156+ }
11491157 }
11501158
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 ::
11531212
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.
11551218
11561219Modifying Embedded Properties with the "exposed" Option
11571220-------------------------------------------------------
@@ -1636,3 +1699,4 @@ bound to Symfony's BC policy for the moment.
16361699.. _`dependent form fields` : https://symfony.com/doc/current/form/dynamic_form_modification.html#dynamic-generation-for-submitted-forms
16371700.. _`Symfony UX configured in your app` : https://symfony.com/doc/current/frontend/ux.html
16381701.. _`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