Search code examples
jquerysymfonyprototypejs

Form is not embeded with prototype while implementing the symfony form collection example in case whole form is rendered at once


I am trying to follow example posted on page https://symfony.com/doc/master/form/form_collections.html. I am trying to embed into Article form an Author form.

My ArticleType (form class) looks like this:

 $builder
/.../
            ->add('authors', CollectionType::class, [
            'entry_type' => AuthorType::class,
            'entry_options' => ['label' => false], 'allow_add' => true])

which results in the HTML code listed in the snipped bellow.

I have added also added a jQuery trying to follow the example. Unluckily that does not seem to work. The authors form is not embedded into Article form.

It seems that $collectionHolder = $('ul.authors'); is invalid as there is no <ul class="authors"> html tag. Instead prototype code is in <div>.

This is well described in the documentation:

If you render your whole "tags" sub-form at once (e.g. form_row(form.tags)), then the prototype is automatically available on the outer div as the data-prototype attribute, similar to what you see above.

However still I am not sure how to modify the code so that it works.

<form name="article" method="post" novalidate="novalidate"><legend class="col-form-label required">Authors</legend><div id="article_authors" data-prototype="&lt;fieldset class=&quot;form-group&quot;&gt;&lt;div id=&quot;article_authors___name__&quot;&gt;&lt;div class=&quot;form-group&quot;&gt;&lt;label for=&quot;article_authors___name___firstName&quot; class=&quot;required&quot;&gt;First name&lt;/label&gt;&lt;input type=&quot;text&quot; id=&quot;article_authors___name___firstName&quot; name=&quot;article[authors][__name__][firstName]&quot; required=&quot;required&quot; class=&quot;form-control&quot; /&gt;&lt;/div&gt;&lt;div class=&quot;form-group&quot;&gt;&lt;label for=&quot;article_authors___name___surname&quot; class=&quot;required&quot;&gt;Surname&lt;/label&gt;&lt;input type=&quot;text&quot; id=&quot;article_authors___name___surname&quot; name=&quot;article[authors][__name__][surname]&quot; required=&quot;required&quot; class=&quot;form-control&quot; /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/fieldset&gt;"></div><div class="form-group"><label class="" for="article_issue">Issue</label><select id="article_issue" name="article[issue]" class="form-control"><option value=""></option><option value="1">KPSE_12019</option><option value="2">KPSET1-1</option><option value="3">Wyd22019</option></select></div><div class="form-group"><button type="submit" id="article_save" name="article[save]" class="btn-primary btn">Zapisz</button></div><input type="hidden" id="article__token" name="article[_token]" value="CbZ7q9gSQKkG9RlxRlcMv6Tt_4ocsJTD5Mgo08xRjPQ" /></div>
</form>

var $collectionHolder;

// setup an "add a tag" link
var $addAuthorButton = $('<button type="button" class="add_author_link">Dodaj autora</button>');
var $newLinkLi = $('<li></li>').append($addAuthorButton);

jQuery(document).ready(function() {
    // Get the ul that holds the collection of authors
    $collectionHolder = $('ul.authors');

    // add the "add an author" anchor and li to the tags ul
    $collectionHolder.append($newLinkLi);

    // count the current form inputs we have (e.g. 2), use that as the new
    // index when inserting a new item (e.g. 2)
    $collectionHolder.data('index', $collectionHolder.find(':input').length);

    $addAuthorButton.on('click', function(e) {
        // add a new tag form (see next code block)
        addAuthorForm($collectionHolder, $newLinkLi);
    });
});

function addAuthorForm($collectionHolder, $newLinkLi) {
    // Get the data-prototype explained earlier
    var prototype = $collectionHolder.data('prototype');

    // get the new index
    var index = $collectionHolder.data('index');

    var newForm = prototype;
    // You need this only if you didn't set 'label' => false in your tags field in TaskType
    // Replace '__name__label__' in the prototype's HTML to
    // instead be a number based on how many items we have
    // newForm = newForm.replace(/__name__label__/g, index);

    // Replace '__name__' in the prototype's HTML to
    // instead be a number based on how many items we have
    newForm = newForm.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);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form name="article" method="post" novalidate="novalidate">
<div id="article"><div class="form-group"><label for="article_namePl" class="required">Name pl</label><textarea id="article_namePl" name="article[namePl]" required="required" class="form-control"></textarea></div><div class="form-group"><label for="article_nameEn" class="required">Name en</label><textarea id="article_nameEn" name="article[nameEn]" required="required" class="form-control"></textarea></div><div class="form-group"><label for="article_summaryPl" class="required">Summary pl</label><textarea id="article_summaryPl" name="article[summaryPl]" required="required" class="form-control"></textarea></div><div class="form-group"><label for="article_summaryEn" class="required">Summary en</label><textarea id="article_summaryEn" name="article[summaryEn]" required="required" class="form-control"></textarea></div><div class="form-group"><label for="article_keywordsPl" class="required">Keywords pl</label><textarea id="article_keywordsPl" name="article[keywordsPl]" required="required" class="form-control"></textarea></div><div class="form-group"><label for="article_keywordsEn" class="required">Keywords en</label><textarea id="article_keywordsEn" name="article[keywordsEn]" required="required" class="form-control"></textarea></div><fieldset class="form-group"><legend class="col-form-label required">Publication date</legend><div id="article_publicationDate" class="form-inline"><div class="sr-only">
                <label class="required" for="article_publicationDate_year">Year</label>
                <label class="required" for="article_publicationDate_month">Month</label>
                <label class="required" for="article_publicationDate_day">Day</label>
            </div><select id="article_publicationDate_month" name="article[publicationDate][month]" class="form-control"><option value="1">Jan</option><option value="2">Feb</option><option value="3">Mar</option><option value="4">Apr</option><option value="5">May</option><option value="6">Jun</option><option value="7">Jul</option><option value="8">Aug</option><option value="9">Sep</option><option value="10">Oct</option><option value="11">Nov</option><option value="12">Dec</option></select><select id="article_publicationDate_day" name="article[publicationDate][day]" class="form-control"><option value="1">1</option><option value="2">2</option><option value="3">3</option><option value="4">4</option><option value="5">5</option><option value="6">6</option><option value="7">7</option><option value="8">8</option><option value="9">9</option><option value="10">10</option><option value="11">11</option><option value="12">12</option><option value="13">13</option><option value="14">14</option><option value="15">15</option><option value="16">16</option><option value="17">17</option><option value="18">18</option><option value="19">19</option><option value="20">20</option><option value="21">21</option><option value="22">22</option><option value="23">23</option><option value="24">24</option><option value="25">25</option><option value="26">26</option><option value="27">27</option><option value="28">28</option><option value="29">29</option><option value="30">30</option><option value="31">31</option></select><select id="article_publicationDate_year" name="article[publicationDate][year]" class="form-control"><option value="2014">2014</option><option value="2015">2015</option><option value="2016">2016</option><option value="2017">2017</option><option value="2018">2018</option><option value="2019">2019</option><option value="2020">2020</option><option value="2021">2021</option><option value="2022">2022</option><option value="2023">2023</option><option value="2024">2024</option></select></div></fieldset><div class="form-group"><label class="required" for="article_status">Status</label><select id="article_status" name="article[status]" class="form-control"><option value="Draft">Draft</option><option value="Submited">Submited</option><option value="Accepted">Accepted</option><option value="Published">Published</option></select></div><fieldset class="form-group"><legend class="col-form-label required">Authors</legend><div id="article_authors" data-prototype="&lt;fieldset class=&quot;form-group&quot;&gt;&lt;div id=&quot;article_authors___name__&quot;&gt;&lt;div class=&quot;form-group&quot;&gt;&lt;label for=&quot;article_authors___name___firstName&quot; class=&quot;required&quot;&gt;First name&lt;/label&gt;&lt;input type=&quot;text&quot; id=&quot;article_authors___name___firstName&quot; name=&quot;article[authors][__name__][firstName]&quot; required=&quot;required&quot; class=&quot;form-control&quot; /&gt;&lt;/div&gt;&lt;div class=&quot;form-group&quot;&gt;&lt;label for=&quot;article_authors___name___surname&quot; class=&quot;required&quot;&gt;Surname&lt;/label&gt;&lt;input type=&quot;text&quot; id=&quot;article_authors___name___surname&quot; name=&quot;article[authors][__name__][surname]&quot; required=&quot;required&quot; class=&quot;form-control&quot; /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/fieldset&gt;"></div></fieldset><div class="form-group"><label class="" for="article_issue">Issue</label><select id="article_issue" name="article[issue]" class="form-control"><option value=""></option><option value="1">KPSE_12019</option><option value="2">KPSET1-1</option><option value="3">Wyd22019</option></select></div><div class="form-group"><button type="submit" id="article_save" name="article[save]" class="btn-primary btn">Zapisz</button></div><input type="hidden" id="article__token" name="article[_token]" value="CbZ7q9gSQKkG9RlxRlcMv6Tt_4ocsJTD5Mgo08xRjPQ" /></div>
</form>


Solution

  • var $collectionHolder;
    
    // setup an "add a tag" link
    var $addAuthorButton = $('<button type="button" class="add_author_link">Dodaj autora</button>');
    var $newLinkLi = $('<div></div>').append($addAuthorButton);
    
    jQuery(document).ready(function() {
        // Get the ul that holds the collection of authors
        $collectionHolder = $('div#article_authors');
    
        // add the "add an author" anchor and li to the tags ul
        $collectionHolder.append($newLinkLi);
    
        // count the current form inputs we have (e.g. 2), use that as the new
        // index when inserting a new item (e.g. 2)
        $collectionHolder.data('index', $collectionHolder.find(':input').length);
    
        $addAuthorButton.on('click', function(e) {
            // add a new tag form (see next code block)
            addAuthorForm($collectionHolder, $newLinkLi);
        });
    });
    
    function addAuthorForm($collectionHolder, $newLinkLi) {
        // Get the data-prototype explained earlier
        var prototype = $collectionHolder.data('prototype');
    
        // get the new index
        var index = $collectionHolder.data('index');
    
        var newForm = prototype;
        // You need this only if you didn't set 'label' => false in your tags field in TaskType
        // Replace '__name__label__' in the prototype's HTML to
        // instead be a number based on how many items we have
            newForm = newForm.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);
    }
    li *{
        display:inline-block;
        margin:0 10px;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
    <form name="article" method="post" novalidate="novalidate"><legend class="col-form-label required">Authors</legend><div id="article_authors" data-prototype="&lt;fieldset class=&quot;form-group&quot;&gt;&lt;div id=&quot;article_authors___name__&quot;&gt;&lt;div class=&quot;form-group&quot;&gt;&lt;label for=&quot;article_authors___name___firstName&quot; class=&quot;required&quot;&gt;First name&lt;/label&gt;&lt;input type=&quot;text&quot; id=&quot;article_authors___name___firstName&quot; name=&quot;article[authors][__name__][firstName]&quot; required=&quot;required&quot; class=&quot;form-control&quot; /&gt;&lt;/div&gt;&lt;div class=&quot;form-group&quot;&gt;&lt;label for=&quot;article_authors___name___surname&quot; class=&quot;required&quot;&gt;Surname&lt;/label&gt;&lt;input type=&quot;text&quot; id=&quot;article_authors___name___surname&quot; name=&quot;article[authors][__name__][surname]&quot; required=&quot;required&quot; class=&quot;form-control&quot; /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/fieldset&gt;">
      
    </div>
    
    <div class="form-group"><label class="" for="article_issue">Issue</label><select id="article_issue" name="article[issue]" class="form-control"><option value=""></option><option value="1">KPSE_12019</option><option value="2">KPSET1-1</option><option value="3">Wyd22019</option></select></div><div class="form-group"><button type="submit" id="article_save" name="article[save]" class="btn-primary btn">Zapisz</button></div><input type="hidden" id="article__token" name="article[_token]" value="CbZ7q9gSQKkG9RlxRlcMv6Tt_4ocsJTD5Mgo08xRjPQ" /></div>
    </form>