Let’s say you are creating a form in Symfony that has some dynamic fields that you need to allow the user to add and remove a subset of options and fields from within the form. Enabling dynamic forms in Symfony is a bit confusing to those just getting their feet wet. Here’s a hint: You have to use data prototypes and a little bit of Javascript. I prefer JQuery, but it doesn’t matter as long as the behavior ends up with the same result of “cloning” the prototype HTML into the proper place on the form with a serialized incrementation of its field name values.
Symfony relies on Javascript for dynamic forms?! Say it isn’t so, Fabien!
Well, every framework and those home rolled models all employ the same basic tactic where parts of a form that may “grow” need to first be hidden from the user and serialized as the user adds new fields. Think of a blogger adding multiple “tags” to a post, or someone needing to leave multiple notes in someone’s medical record.
The fact is that dynamic forms rely on the client side to allow users to add and remove new elements to and from a form so it’s a good initial test to see if they have the capability by initializing a hidden element and taking the “prototype” data out of the “data-prototype” tag that Symfony generates and unescape the embedded HTML and putting it into it’s new visible location within the dynamic form. By the way, another good practice would be to design the form with “graceful degradation” so those who are blocking Javascript or don’t have it on their browser for some strange reason can still use the forms basically as initially intended, but that choice is up to you — and the people paying the bills. After all, time is money.
For all those who’ve Googled and been on Stack Overflow and Binged your way onto this god-forsaken page in the middle of the great cyber void, if you need to access prototype information in order to render out certain tags the prototype html generates inside a hidden element with a “data-prototype” tag and you can’t rely on JavaScript and plain CSS to format the label and fields or maybe you just need more control over formatting, this is the general pattern to access such elements inside Twig. Just replace “blogPost” with the name of the form and “tagName” with the name of the form field.
<div id="clone-tag-form"> <div class="formatting-tag-couldnt-do-before"> {{ form_label(form.blogPost.vars.prototype.tagName) }} </div> {{ form_widget(form.blogPost.vars.prototype.tagName) }} </div>
Keep in mind that this gives you more granular control over the part of a form that will be cloned so it’s up to you to take the HTML inside “clone-tag-form” and use a placeholder in the field name values such as “__NAME__” and replace these with serialized, incremented numbers so when processed the data won’t confuse the form handler and data won’t get lost. It’s an all or nothing proposition. Others suggest making a prototype template and binding the results from the prototype output to the template like so, but it just gets a little more complicated:
Idea adapted from Stack Overflow:
The idea is simply to render the collection items through a Twig template, so you can customize the prototype that will be placed in your
data-prototype="..."
tag as if it was a normal form.
In yourMainForm.html.twig:
<span class="tag"><div</span><span class="atn">id</span><span class="pun">=</span><span class="atv">"collectionCont"</span><span class="atn">data-prototype</span><span class="pun">=</span><span class="atv">" {% filter escape %} {{ include('MyBundle:MyViewsDir:prototype.html.twig', { 'form': form.myForm.vars.prototype }) }} {% endfilter %}}"</span><span class="tag">></span><span class="tag"></div></span>And in MyBundle:MyViewsDir:prototype.html.twig:
<span class="tag"><div></span><span class="com"><!-- customize as needed --></span><span class="pln"> {{ form_label(form.field1) }} {{ form_widget(form.field1) }} {{ form_label(form.field2) }} {{ form_widget(form.field2) }} </span><span class="tag"></div></span>