Применение в Angular

Для интеграции Dragula в Angular используется официальная обёртка ng2-dragula, которая обеспечивает удобный доступ к функционалу Dragula через Angular-сервисы и директивы. Установка производится через npm:

npm install dragula ng2-dragula --save

Импорт модуля в корневой модуль приложения:

import { DragulaModule } from 'ng2-dragula';

@NgModule({
  imports: [
    DragulaModule.forRoot()
  ]
})
export class AppModule { }

После этого Dragula готов к использованию в компонентах.

Создание контейнеров для перетаскивания

Dragula оперирует контейнерами, между которыми осуществляется перетаскивание элементов. В Angular создаются обычные HTML-элементы с директивой dragula:

<div class="container" [dragula]="'TASKS'">
  <div *ngFor="let task of tasks">{{ task.name }}</div>
</div>

<div class="container" [dragula]="'TASKS'">
  <div *ngFor="let task of completedTasks">{{ task.name }}</div>
</div>

Ключевой момент: идентификатор 'TASKS' связывает контейнеры, позволяя перемещать элементы между ними. Можно создавать несколько групп с разными идентификаторами для разделения зон перетаскивания.

Настройка опций Dragula

Dragula поддерживает множество опций, которые задаются через сервис DragulaService. Это позволяет гибко управлять поведением перетаскивания. Пример конфигурации:

import { DragulaService } from 'ng2-dragula';

@Component({...})
export class TasksComponent {
  constructor(private dragulaService: DragulaService) {
    this.dragulaService.createGroup('TASKS', {
      moves: (el, source, handle, sibling) => handle.classList.contains('handle'),
      copy: false,
      revertOnSpill: true,
      direction: 'vertical'
    });
  }

  ngOnDestroy() {
    this.dragulaService.destroy('TASKS');
  }
}

Ключевые параметры:

  • moves: определяет, какой элемент может быть захвачен для перетаскивания.
  • copy: разрешает или запрещает копирование элементов вместо перемещения.
  • revertOnSpill: возвращает элемент на исходное место при ошибочном переносе.
  • direction: задаёт направление перетаскивания ('vertical' или 'horizontal').

Работа с событиями

Dragula предоставляет набор событий, которые позволяют отслеживать состояние перетаскивания и изменять данные Angular. Основные события:

  • drag: элемент начал перетаскиваться.
  • drop: элемент был перемещён в новый контейнер.
  • over: элемент над контейнером.
  • out: элемент покинул контейнер.
  • remove: элемент удалён через бросок за пределы контейнера.
  • shadow: создаётся тень элемента при перемещении.

Пример подписки на события:

this.dragulaService.dropModel('TASKS').subscribe(({ source, target, item, sourceModel, targetModel, index }) => {
  console.log('Элемент перемещён:', item);
  console.log('Новая позиция в модели:', index);
});

Использование dropModel и других *Model событий позволяет автоматически синхронизировать данные в Angular с визуальным состоянием.

Динамическое управление группами

Dragula позволяет создавать и удалять группы динамически. Это полезно для компонентов с изменяющейся структурой контейнеров:

addGroup(name: string) {
  this.dragulaService.createGroup(name, {
    revertOnSpill: true
  });
}

removeGroup(name: string) {
  this.dragulaService.destroy(name);
}

Динамическое управление группами особенно важно в больших приложениях с множеством зон перетаскивания.

Синхронизация с моделями Angular

Dragula работает не только с DOM, но и с массивами Angular. Для этого используется привязка через *ngFor и события dropModel/removeModel. Это гарантирует, что любые перемещения элементов будут автоматически отражены в данных:

<div [dragula]="'TASKS'" [dragulaModel]="tasks">
  <div *ngFor="let task of tasks">{{ task.name }}</div>
</div>

Перемещение элемента автоматически обновляет массив tasks. Таким образом, визуальные изменения и состояние модели остаются синхронизированными без дополнительного кода.

Продвинутые сценарии

  • Перетаскивание с фильтрацией элементов: moves и accepts позволяют запрещать перемещение определённых элементов или их попадание в конкретные контейнеры.
  • Анимация перетаскивания: с помощью CSS можно стилизовать .gu-mirror, .gu-transit и .gu-hide для плавных анимаций.
  • Асинхронное обновление данных: при загрузке данных с сервера события Dragula можно комбинировать с BehaviorSubject или Observable для динамического обновления списков.
  • Драг-энд-дроп между различными компонентами: уникальные идентификаторы групп и dropModel обеспечивают перемещение элементов между компонентами без потери данных.

Стилизация элементов

Dragula добавляет классы для управления состоянием перетаскивания:

  • .gu-mirror — копия элемента, которая перемещается по экрану.
  • .gu-transit — элемент в процессе перетаскивания.
  • .gu-hide — скрывает оригинальный элемент при перетаскивании.
  • .gu-unselectable — предотвращает выделение текста при захвате элемента.

Использование этих классов в сочетании с CSS-переходами и анимациями позволяет создавать визуально привлекательные интерфейсы перетаскивания.

Интеграция с формами и валидацией

Dragula можно использовать внутри Angular-форм. События dropModel можно подключать к реактивным формам, обновляя FormArray при перемещении элементов:

this.dragulaService.dropModel('TASKS').subscribe(({ sourceModel, targetModel }) => {
  this.form.setControl('tasks', new FormArray(targetModel.map(task => new FormControl(task))));
});

Это обеспечивает полную совместимость с механизмами валидации и сохранения состояния формы, не нарушая принципов реактивного программирования Angular.