Я пытался следовать этот ответ для обработки вложенных коллекций в формах.
у меня есть 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')
;
}
Теперь я хотел бы обработать добавление / удаление для каждой коллекции …
Как я уже сказал, я пытался следовать этот ответ
Для этого я попытался:
{% 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.
Мне действительно нужна помощь .. Коллекции — мой кошмар …
Спасибо !
Позвольте мне начать с извинений, потому что мой ответ в упомянутом вами посте был явно неправильным 🙂 По крайней мере пример кода был неправильным. Интересно, нашел ли оригинальный постер правильное решение?
Ошибка достаточно легко заметить. В Twig вы используете переменную ports
Но как же Твиг знать, откуда он?
{% 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 (который я написал) не может справиться с этим так просто, как вы уже пробовали. Он будет нормально обрабатывать верхнюю коллекцию, но не будет автоматически создавать экземпляры коллекций при загрузке / создании / удалении. Помните, что каждая коллекция servLiens имеет коллекцию портов. Поэтому уже при загрузке вы не можете просто загрузить col-ports
как вы делаете сейчас, но нужно будет загрузить каждую коллекцию портов. Затем вам нужно будет создавать новую коллекцию FormCollection в коллекции портов каждый раз, когда вы добавляете строку servLiens.
У меня нет времени, чтобы создать ваш код для вас, но я надеюсь, что мое объяснение поможет вам найти решение.
Похоже, вы используете Symfony 2.6 или старше.
Я бы начал с того, что ты не должен делать
"'type' => new PortLienAppliServType ()"
но передайте имя типа формы. Symfony будет создавать его и мог быть проблемой.
Все подробно описано здесь: https://symfony.com/doc/2.6/reference/forms/types/collection.html#basic-usage
Честно говоря, Коллекции не так уж и сложны, вам просто нужно понять, как это работает, тогда Symfony все сделает за вас.