I have a form with a collection in Symfony 6 where it is possible to add and delete items. Adding and removing a collection works. The problem is only if an error is rendered in the form ($form->addError(new FormError('Some Error'));
). At that point, the link to remove the collection will be lost, but the items will still be rendered. What is needed so that the "remove" link does not disappear?
const addTagLink = document.createElement('a')
addTagLink.classList.add('add')
addTagLink.href = '#'
addTagLink.innerText = 'Add'
addTagLink.dataset.collectionHolderClass = 'items'
const collectionHolder = document.querySelector('ul.items')
collectionHolder.appendChild(addTagLink)
const addFormToCollection = (e) => {
const collectionHolder = document.querySelector('.' e.currentTarget.dataset.collectionHolderClass);
const item = document.createElement('li');
item.innerHTML = collectionHolder
.dataset
.prototype
.replace(
/__name__/g,
collectionHolder.dataset.index
);
collectionHolder.appendChild(item);
collectionHolder.dataset.index ;
const removeLink = document.createElement('a');
removeLink.href = '#';
removeLink.innerText = 'Remove';
removeLink.classList.add('remove');
item.appendChild(removeLink);
removeLink.addEventListener('click', (e) => {
e.preventDefault();
item.remove();
})
}
addTagLink.addEventListener("click", addFormToCollection)
<form name="expedition" method="post">
<div>
<ul data-index="0" data-prototype="<div id="expedition_items___name__"><div><label class="block text-sm font-medium text-gray-700 required" for="expedition_items___name___quantity">Quantity</label><input type="text" id="expedition_items___name___quantity" name="expedition[items][__name__][quantity]" required="required" class="block w-full shadow-sm border-gray-300 rounded-md border p-2 mt-1 mb-2" inputmode="decimal" /></div><div><label class="block text-sm font-medium text-gray-700 required" for="expedition_items___name___product">Product</label><select id="expedition_items___name___product" name="expedition[items][__name__][product]" required="required" class="block w-full shadow-sm border-gray-300 rounded-md border p-2 mt-1 mb-2"><option value="" selected="selected">Choose a product</option><option value="2">Product 1</option><option value="3">Product 2</option></select></div></div>">
</ul>
</div>
<button type="submit">Save</button>
</form>
twig
{{ form_start(form) }}
{% if form_errors(form) %}
<div >{{ form_errors(form) }}</div>
{% endif %}
<div>
<ul
data-index="{{ form.items|length > 0 ? form.items|last.vars.name 1 : 0 }}"
data-prototype="{{ form_widget(form.items.vars.prototype)|e('html_attr') }}">
<li >
{{ form_label(form.items) }}
{{ form_widget(form.items) }}
{{ form_errors(form.items) }}
<li>
</ul>
</div>
<button type="submit" >{{ 'Save'|trans }}</button>
{{ form_end(form)}}
CodePudding user response:
Render your items in a loop to give each item its own <li>
:
<ul
data-index="{{ form.items|length > 0 ? form.items|last.vars.name 1 : 0 }}"
data-prototype="{{ form_widget(form.items.vars.prototype)|e('html_attr') }}">
{% for itemForm in form.items %}
<li >
{{ form_label(itemForm) }}
{{ form_widget(itemForm) }}
{{ form_errors(itemForm) }}
</li>
{% endfor %}
</ul>
Then create a JavaScript function that adds a remove link to a given item:
const addItemFormDeleteLink = (item) => {
const removeLink = document.createElement('a');
removeLink.href = '#';
removeLink.innerText = 'Remove';
removeLink.classList.add('remove');
item.appendChild(removeLink);
removeLink.addEventListener('click', (e) => {
e.preventDefault();
item.remove();
})
}
And use this to add the remove link to the existing items on the page:
document
.querySelectorAll('ul.items li')
.forEach((tag) => {
addItemFormDeleteLink(tag)
})
And in addFormToCollection
:
const addFormToCollection = (e) => {
const collectionHolder = document.querySelector('.' e.currentTarget.dataset.collectionHolderClass);
const item = document.createElement('li');
item.innerHTML = collectionHolder
.dataset
.prototype
.replace(
/__name__/g,
collectionHolder.dataset.index
);
collectionHolder.appendChild(item);
collectionHolder.dataset.index ;
// add a delete link to the new form
addItemFormDeleteLink(item);
}
See also: https://symfony.com/doc/current/form/form_collections.html#allowing-tags-to-be-removed