Skip to content

Multiple click event listeners get added to delete buttons on collections #7625

@Deksor

Description

@Deksor

Describe the bug
Easyadmin adds repeatedly the same 'click' event listener on "delete" buttons for collection items and they stack with each push to the list.

For a given list, if you add one element, it will have a delete button with one listener.
If you add a second one, the first element will now have two listeners, the first one will keep just one.
If you add a third one, the first element will have three listeners, the second element will have two, and the third one will have one.

This then causes even more issue afterwards, because when clicking on 'delete' for the first element, it will fire the same delete event 3 times. Furthermore now that it communicates the collection it belongs to, on the first strike, it will put the collection as it should in the event. But then on the second and third strike, since it's already been deleted, it has no parent anymore, therefore the collection becomes null.

To Reproduce
I used EA 4.29.8, but looking at the code, version 5 is also impacted. As stated before, you just need to add more than one element to the list and look at the event listeners on the delete button of any element who isn't the last one added.

(OPTIONAL) Additional context
Here's a screenshot from my firefox

Image

I've already figured out that the bug is caused by this part (https://github.com/EasyCorp/EasyAdminBundle/blob/5.x/assets/js/field-collection.js#L1)

const eaCollectionHandler = (event) => {
    document.querySelectorAll('button.field-collection-add-button').forEach((addButton) => {
        const collection = addButton.closest('[data-ea-collection-field]');

        if (!collection || collection.classList.contains('processed')) {
            return;
        }

        EaCollectionProperty.handleAddButton(addButton, collection);
        EaCollectionProperty.updateCollectionItemCssClasses(collection);
    });

    document.querySelectorAll('button.field-collection-delete-button').forEach((deleteButton) => {
        deleteButton.addEventListener('click', () => {
            const collection = deleteButton.closest('[data-ea-collection-field]');
            const item = deleteButton.closest('.field-collection-item');

            item.remove();
            document.dispatchEvent(
                new CustomEvent('ea.collection.item-removed', { detail: { deletedElement: item, collection } })
            );

            EaCollectionProperty.updateCollectionItemCssClasses(collection);
        });
    });
};

eaCollectionHandler is called for each 'item added' event (and also DOMContentLoaded), then it will iterate on each delete button to add a click listener, without checking if it was already added before or not.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions