templates/js/library/entity_collection/entity_collection.js.twig line 1

Open in your IDE?
  1. /**
  2.  * Класс для управления коллекцией сущностей (добавление/удаление элементов).
  3.  * Требуется jQuery.
  4.  */
  5. class EntityCollection {
  6.     /**
  7.      * @param {HTMLElement|jQuery} element Корневой элемент коллекции
  8.      */
  9.     constructor(element) {
  10.         this.collectionName = $(element).data('collection-name') || null;
  11.         /** @private */
  12.         this.$element = $(element);
  13.         /** @private */
  14.         this.$itemsContainer = this.$element.find('.js-entity-collection-items[data-collection-name="' + this.collectionName + '"]');
  15.         /** @private */
  16.         this.prototypeName = this.$element.data('prototype-name') || '__name__';
  17.         /** @private {string|null} */
  18.         this.prototypeHtml = this.$element.data('prototype') || null;
  19.         /** @private {string} */
  20.         this.itemClass = this.$element.data('item-class') || 'entity-collection__item';
  21.         /** @private {number} */
  22.         this.index = this.$itemsContainer.find('.' + this.itemClass).length || this.$itemsContainer.children().length || 0;
  23.         this.init();
  24.     }
  25.     /**
  26.      * Инициализация обработчиков событий.
  27.      * @private
  28.      */
  29.     init() {
  30.         // Добавление элемента
  31.         this.$element.on('click', '.js-entity-collection-add', (event) => {
  32.             const $button = $(event.currentTarget);
  33.             const btnName = $button.data('collection-name');
  34.             const containerName = this.$element.data('collection-name');
  35.             // Если кнопка явно привязана к другой коллекции — игнорируем клик
  36.             if (btnName != null && String(btnName) !== String(containerName)) {
  37.                 return;
  38.             }
  39.             this.addItem();
  40.         });
  41.         // Удаление элемента — поддерживаем старые и новые селекторы кнопки удаления
  42.         this.$element.on('click', '.js-entity-collection-remove', (event) => {
  43.             this.removeItem(event);
  44.         });
  45.         this.updateItemsHeaders();
  46.     }
  47.     /**
  48.      * Удаляет элемент коллекции, родственный элементу-селектору удаления.
  49.      * @param {Event} event
  50.      */
  51.     removeItem(event) {
  52.         const $button = $(event.currentTarget);
  53.         const $item = $button.closest('.' + this.itemClass);
  54.         if ($item.length) {
  55.             $item.remove();
  56.             this.updateItemsHeaders();
  57.         }
  58.     }
  59.     /**
  60.      * Добавляет новый элемент в коллекцию, используя prototypeHtml.
  61.      */
  62.     addItem() {
  63.         if (!this.prototypeHtml) {
  64.             // Используем console.warn — это не должно ломать выполнение
  65.             console.warn('EntityCollection: prototypeHtml is missing');
  66.             return;
  67.         }
  68.         const newForm = this.prototypeHtml.replace(new RegExp(this.prototypeName, 'g'), this.index);
  69.         const $wrapper = $('<div></div>').addClass(this.itemClass + ' mb-3 p-3 border rounded');
  70.         $wrapper.html(newForm);
  71.         this.$itemsContainer.append($wrapper);
  72.         this.index += 1;
  73.         this.updateItemsHeaders();
  74.     }
  75.     updateItemsHeaders() {
  76.         this.$itemsContainer.find('.entity-collection-item-header[data-collection-name="'
  77.             + this.collectionName + '"]').each((index, header) => {
  78.             let text = $(header).data('header-text');
  79.             if (text) {
  80.                 text = text.replaceAll('{itemIndex}', (index + 1));
  81.                 $(header).html(text);
  82.             }
  83.         });
  84.     }
  85. }