I am trying to create a form containing nested collections. I do not know how to process the JS part to display a collection of children. Does anyone know how I can do this?
Here is the code for my forms:
class ParentFormType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('case', 'choice', array( 'choices' => array( 'case1' => 'case1', 'case2' => 'case2', 'case3' => 'case3', ))) ->add ('subForm1', 'collection', array ( 'type' => new Sub1FormType(), 'allow_add' => true, 'allow_delete' => true, 'by_reference' => false, 'prototype' => true, )) ; $builder->add('save',"submit") ; } public function setDefaultOptions(OptionsResolverInterface $resolver) { } public function getName() { return 'formtestparenttype'; } } class Sub1FormType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('fieldSub1',"text" ) ->add ('childForm1', 'collection', array ( 'type' => new Sub2FormType, 'allow_add' => true, 'allow_delete' => true, 'by_reference' => false, 'prototype' => true, )) ; } public function setDefaultOptions(OptionsResolverInterface $resolver) { } public function getName() { return 'formtestsub1type'; } } class Sub2FormType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('fieldSub2',"text" ) ; } public function setDefaultOptions(OptionsResolverInterface $resolver) { } public function getName() { return 'formtesttype'; } }
Controller:
$form = $this->createForm(new ParentFormType() ) ; return $this->render('MyBundle:Test:test.html.twig', array( 'form' => $form->createView() ));
And here; part of twig + js:
{% extends '::base.html.twig' %} {% block content %} {{ form_start(form) }} <h3>Tags</h3> <ul class="collectionHolder" data-prototype="{{ form_widget(form.subForm1.vars.prototype)|e }}"> {# iterate over each existing tag and render its only field: name #} {% for subForm1 in form.subForm1 %} <li>{{ form_row(subForm1) }} </li> <ul class="collectionHolder" data-prototype="{{ form_widget(subForm2.vars.prototype)|e }}"> {%for subForm2 in subForm1.subForm2 %} <li>{{ form_row(subForm2) }}</li> {% endfor %} {% endfor %} </ul> {{ form_end(form) }} <script> var $collectionHolder; // setup an "add a tag" linkd var $addTagLink = $('<a href="#" class="add_tag_link">Add</a>'); var $newLinkLi = $('<li></li>').append($addTagLink); jQuery(document).ready(function() { function addTagForm($collectionHolder, $newLinkLi) { // Get the data-prototype explained earlier var prototype = $collectionHolder.data('prototype'); // get the new index var index = $collectionHolder.data('index'); // Replace '__name__' in the prototype HTML to // instead be a number based on how many items we have var newForm = prototype.replace(/__name__/g, index); // increase the index with one for the next item $collectionHolder.data('index', index + 1); // Display the form in the page in an li, before the "Add a tag" link li var $newFormLi = $('<li></li>').append(newForm); $newLinkLi.before($newFormLi); } $collectionHolder = $('.collectionHolder'); $collectionHolder.append($newLinkLi); // count the current form inputs we have (eg 2), use that as the new // index when inserting a new item (eg 2) $collectionHolder.data('index', $collectionHolder.find(':input').length); $addTagLink.on('click', function(e) { e.preventDefault(); addTagForm($collectionHolder, $newLinkLi); }); }); </script> {% endblock content %}