表单中的嵌套集合

时间:2017-10-31 15:47:51

标签: php forms symfony collections

我尝试按照this回答来处理表单中的嵌套集合。

我有Application的表单,其中包含LienAppliServ表格的集合:

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('servLiens', 'collection', array(
            'label' => ' ',
            'type' => new LienAppliServType(),
            'allow_add' => true,
            'allow_delete' => true,
            'by_reference' =>false,
            'prototype' => true,
        ))
    //...

在我的LienAppliServ表单中,我有另一个PortLienAppliServ表单集合:

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('ports', 'collection', array(
            'type' => new PortLienAppliServType(),
            'allow_add' => true,
            'allow_delete' => true,
            'prototype' => true,
            'by_reference' =>false
        ))
    //...

PortLienAppliServ的形式是:

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('numPort')
        ->add('type')
    ;
}

现在,我想处理每个集合的添加/删除... 正如我所说,我试图遵循this回答

为了做到这一点,我尝试了:

{% block body -%}
    {{ form_start(form) }}

            <ul id="col-servliens" data-prototype="{{ form_widget(form.servLiens.vars.prototype)|e }}">
                {# iterate over each existing tag and render its only field: name #}
                {% for servLiens in form.servLiens %}
                    <li>{{ form_row(servLiens) }} </li>
                    <ul id="col-ports" data-prototype="{{ form_widget(ports.vars.prototype)|e }}">
                    {%for ports in servLiens.ports %}
                        <li>{{ form_row(ports) }}</li>
                    {% endfor %}

                {% endfor %}
            </ul>
        {{ form_end(form) }}

{% endblock %}

{% block app_js %}

    //Same as the other question
    <script>
        function FormCollection(div_id)
        {
            // keep reference to self in all child functions
            var self=this;

            self.construct = function () {
                // set some shortcuts
                self.div = $('#'+div_id);
                self.div.data('index', self.div.find(':input').length);

                // add delete link to existing children
                self.div.children().each(function() {
                    self.addDeleteLink($(this));
                });

                // add click event to the Add new button
                self.div.next().on('click', function(e) {
                    // prevent the link from creating a "#" on the URL
                    e.preventDefault();

                    // add a new tag form (see next code block)
                    self.addNew();
                });
            };

            /**
             * onClick event handler -- adds a new input
             */
            self.addNew = function () {
                // Get the data-prototype explained earlier
                var prototype = self.div.data('prototype');

                // get the new index
                var index = self.div.data('index');

                // Replace '__name__' in the prototype's 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
                self.div.data('index', index + 1);

                // Display the form in the page in an li, before the "Add a tag" link li
                self.div.append($(newForm));

                // add a delete link to the new form
                self.addDeleteLink( $(self.div.children(':last-child')[0]) );

                // not a very nice intergration.. but when creating stuff that has help icons,
                // the popovers will not automatically be instantiated
                //initHelpPopovers();

                return $(newForm);
            };

            /**
             * add Delete icon after input
             * @param Element row
             */
            self.addDeleteLink = function (row) {
                var $removeFormA = $('<a href="#" class="btn btn-danger" tabindex="-1"><i class="entypo-trash"></i></a>');
                $(row).find('select').after($removeFormA);
                row.append($removeFormA);
                $removeFormA.on('click', function(e) {
                    // prevent the link from creating a "#" on the URL
                    e.preventDefault();

                    // remove the li for the tag form
                    row.remove();
                });
            };

            self.construct();
        }

    </script>


    <script>
        $(document).ready(function() {
            new FormCollection('col-servliens');
            new FormCollection('col-ports');
        });
    </script>

我得到了

Variable "ports" does not exist.

我真的需要一些帮助..收藏其实是我的噩梦...... 谢谢!

2 个答案:

答案 0 :(得分:0)

让我先道歉,因为我在你提到的帖子中的答案显然是错误的:)至少示例代码不正确。我想知道原始海报是否找到了正确的解决方案。

错误很容易发现。在Twig中你使用变量ports,但Twig应该如何知道它来自哪里?

{% for servLiens in form.servLiens %}
<li>{{ form_row(servLiens) }} </li>
    <ul id="col-ports" data-prototype="{{ form_widget(ports.vars.prototype)|e }}">
        {%for ports in servLiens.ports %}
            <li>{{ form_row(ports) }}</li>
        {% endfor %}
{% endfor %}

使用{{ form_row(servLiens) }}实际上已经创建了集合类,因此不需要它内部的整个<ul>。事实上,Symfony非常聪明,甚至不需要第一个<ul>。它会找到任何子表单(例如集合)并创建所需的所有HTML。

因此,您在代码中唯一需要的是:

{{ form_row(form.servLiens) }}

如果您查看从中生成的HTML,您将看到数据原型和子项。它将自动具有您可以使用的id属性。

问题是javascript部分。您正在使用集合中的集合。 javascript FormCollection类(我写的)无法像你尝试过的那样处理它。它将处理顶级集合,但它不会在load / create / delete上自动实例化集合。请记住,每个servLiens集合都有一组端口。因此,在加载时,您不能像现在一样只加载col-ports,而是需要加载每个端口集合。然后,每次添加servLiens行时,都需要在端口集合上实例化一个新的FormCollection。

我没有时间为您创建代码,但我希望我的解释可以帮助您找到解决方案。

答案 1 :(得分:-1)

您似乎使用symfony 2.6或更早版本。

我首先要说你不应该做

"'type' => new PortLienAppliServType()"
但是传递表单类型名称。 Symfony将实例化它,可能成为问题。

此处的所有内容均在此处进行了描述:https://symfony.com/doc/2.6/reference/forms/types/collection.html#basic-usage

老实说,收藏并不困难,你只需要了解它是如何工作的,然后Symfony会为你管理一切。