Использование с Angular

Для интеграции Muuri с Angular сначала необходимо установить библиотеку через npm:

npm install muuri

После установки рекомендуется использовать ES модули, импортируя Muuri непосредственно в компонент, где будет осуществляться работа с сеткой:

import Muuri from 'muuri';

Angular использует ViewChild и AfterViewInit для взаимодействия с DOM, поэтому создание сетки Muuri производится после инициализации представления компонента.

import { Component, ElementRef, ViewChild, AfterViewInit } from '@angular/core';
import Muuri from 'muuri';

@Component({
  selector: 'app-muuri-grid',
  templateUrl: './muuri-grid.component.html',
  styleUrls: ['./muuri-grid.component.scss']
})
export class MuuriGridComponent implements AfterViewInit {
  @ViewChild('gridContainer', { static: true }) gridContainer!: ElementRef;

  private grid!: Muuri;

  ngAfterViewInit(): void {
    this.grid = new Muuri(this.gridContainer.nativeElement, {
      dragEnabled: true,
      layout: {
        rounding: false
      }
    });
  }
}

Шаблон компонента

HTML-шаблон должен содержать контейнер сетки и элементы, которые будут управляться Muuri. Элементы должны быть обёрнуты в общий контейнер:

<div #gridContainer class="grid">
  <div class="item" *ngFor="let item of items">
    <div class="item-content">{{ item.name }}</div>
  </div>
</div>

CSS для сетки и элементов:

.grid {
  display: flex;
  flex-wrap: wrap;
}

.item {
  width: 150px;
  margin: 10px;
}

.item-content {
  background: #e0e0e0;
  padding: 20px;
  text-align: center;
  border-radius: 8px;
}

Настройка параметров Muuri

Muuri предлагает широкий набор опций, позволяющих управлять поведением сетки:

  • dragEnabled – включает возможность перетаскивания элементов.

  • layout – объект с настройками расположения элементов:

    • fillGaps: true – элементы заполняют пустое пространство.
    • rounding: true/false – округление координат при рендеринге.
  • sortData – функция или объект для сортировки элементов.

  • items – массив DOM-элементов, который можно передать вручную при инициализации.

Пример кастомной сортировки по имени элемента:

this.grid.sort((a, b) => {
  const nameA = a.getElement().textContent?.trim() || '';
  const nameB = b.getElement().textContent?.trim() || '';
  return nameA.localeCompare(nameB);
});

Динамическое обновление сетки

Angular активно использует двустороннее связывание данных, поэтому добавление или удаление элементов требует синхронизации с Muuri. После изменения массива элементов следует вызвать методы:

  • add – добавление нового элемента:
const newItem = document.createElement('div');
newItem.classList.add('item');
newItem.innerHTML = `<div class="item-content">Новый элемент</div>`;
this.grid.add(newItem);
  • remove – удаление элемента:
const item = this.grid.getItems().find(i => i.getElement().textContent?.includes('Новый элемент'));
if (item) {
  this.grid.remove(item, { removeElements: true });
}
  • refreshItems – обновление внутренних данных сетки:
this.grid.refreshItems().layout();

Реализация drag-and-drop

Muuri поддерживает гибкую систему перетаскивания элементов. В Angular можно подключить drag-события через опции:

this.grid = new Muuri(this.gridContainer.nativeElement, {
  dragEnabled: true,
  dragSort: function () {
    return [this.gridContainer.nativeElement];
  },
  dragSortInterval: 50,
  dragStartPredicate: {
    distance: 0,
    delay: 0
  }
});

Для более сложных сценариев, например перемещения элементов между разными компонентами Angular, можно использовать события dragReleaseEnd и dragReleaseStart, которые возвращают DOM-элемент и данные элемента, что позволяет синхронизировать состояние приложения:

this.grid.on('dragReleaseEnd', (item) => {
  item.getElement().style.zIndex = '';
  this.grid.layout();
});

Реактивная интеграция с Angular

Для динамических приложений рекомендуется использовать RxJS для отслеживания изменений массива элементов. Пример:

import { BehaviorSubject } from 'rxjs';

items$ = new BehaviorSubject<Item[]>([]);

this.items$.subscribe(newItems => {
  this.grid.refreshItems().layout();
});

Это позволяет автоматически обновлять сетку при изменении данных в сервисе или состоянии компонента.

Работа с фильтрацией

Muuri поддерживает фильтры через метод filter. Пример фильтрации по категории:

this.grid.filter(item => item.getElement().dataset.category === 'category1');

Можно сочетать фильтры с сортировкой и drag-and-drop, создавая интерактивные интерфейсы с динамическим контентом в Angular.

Производительность и оптимизация

  • Включение layout: { rounding: false } предотвращает лишние перерисовки.
  • Использование requestAnimationFrame внутри событий Angular может улучшить плавность анимаций.
  • Для больших массивов элементов рекомендуется группировать добавления/удаления и вызывать layout один раз после всех изменений.

Особенности работы с Angular Universal

При рендеринге на сервере Muuri не работает, так как он зависит от DOM. Следует использовать условную инициализацию только на клиенте:

if (typeof window !== 'undefined') {
  this.grid = new Muuri(this.gridContainer.nativeElement);
}

Это предотвращает ошибки при SSR и позволяет безопасно интегрировать сетку Muuri в проекты с Angular Universal.