Для работы с библиотекой Shopify Draggable в Angular оптимальным подходом является использование директив, которые инкапсулируют логику перетаскивания и управления событиями, сохраняя компонент чистым и удобным для повторного использования.
Первым шагом необходимо установить библиотеку через npm:
npm install @shopify/draggable
Импортировать основные модули в Angular можно через ES6 модули:
import { Draggable, Sortable, Swappable } from '@shopify/draggable';
Для работы с Angular директивами важно избегать глобальных селекторов и использовать ViewChild или ElementRef для привязки к конкретным элементам DOM.
Директива отвечает за инициализацию экземпляра Draggable
и управление его жизненным циклом.
import { Directive, ElementRef, Input, OnInit, OnDestroy } from '@angular/core';
import { Draggable } from '@shopify/draggable';
@Directive({
selector: '[appDraggable]'
})
export class DraggableDirective implements OnInit, OnDestroy {
@Input() draggableOptions: any;
private draggableInstance: Draggable;
constructor(private el: ElementRef) {}
ngOnInit(): void {
this.draggableInstance = new Draggable(this.el.nativeElement, this.draggableOptions || {});
this.bindEvents();
}
private bindEvents(): void {
this.draggableInstance.on('drag:start', (event) => console.log('Drag started', event));
this.draggableInstance.on('drag:move', (event) => console.log('Dragging', event));
this.draggableInstance.on('drag:stop', (event) => console.log('Drag stopped', event));
}
ngOnDestroy(): void {
if (this.draggableInstance) {
this.draggableInstance.destroy();
}
}
}
Ключевые моменты:
ElementRef обеспечивает безопасный доступ к DOM
элементу.@Input() draggableOptions позволяет настраивать
поведение Draggable напрямую из шаблона.drag:start, drag:move,
drag:stop позволяют реализовать кастомные реакции на
перетаскивание.destroy() гарантирует очистку ресурсов при
удалении компонента.Для сортировки элементов лучше создать отдельную директиву, которая
наследует принципы Draggable, но использует функциональность
Sortable.
import { Directive, ElementRef, Input, OnInit, OnDestroy } from '@angular/core';
import { Sortable } from '@shopify/draggable';
@Directive({
selector: '[appSortable]'
})
export class SortableDirective implements OnInit, OnDestroy {
@Input() sortableOptions: any;
private sortableInstance: Sortable;
constructor(private el: ElementRef) {}
ngOnInit(): void {
this.sortableInstance = new Sortable(this.el.nativeElement, this.sortableOptions || {});
this.bindEvents();
}
private bindEvents(): void {
this.sortableInstance.on('sortable:sorted', (event) => console.log('Sorted', event));
this.sortableInstance.on('sortable:start', (event) => console.log('Sort started', event));
this.sortableInstance.on('sortable:stop', (event) => console.log('Sort stopped', event));
}
ngOnDestroy(): void {
if (this.sortableInstance) {
this.sortableInstance.destroy();
}
}
}
Особенности Sortable:
sortable:sorted для
отслеживания завершения сортировки.DraggableDirective для
расширенных сценариев.<ul appSortable [sortableOptions]="{ draggable: 'li' }">
<li appDraggable [draggableOptions]="{ handle: '.handle' }">
<span class="handle">☰</span> Элемент 1
</li>
<li appDraggable>
Элемент 2
</li>
<li appDraggable>
Элемент 3
</li>
</ul>
Пояснения:
[sortableOptions] передает конфигурацию
Sortable, включая селектор элементов для
перетаскивания.[draggableOptions] задает индивидуальные параметры
перетаскивания, например, handle для ограничения области
захвата.Draggable и
Sortable директивы позволяет реализовать гибкие
интерфейсы.События Draggable и Sortable предоставляют объект события с полезными свойствами:
source — исходный элемент.mirror — клонированный элемент, который
перемещается.over и dragEvent — для контроля
пересечения с другими элементами.Можно использовать RxJS для реактивного управления:
import { fromEventPattern } from 'rxjs';
ngOnInit() {
const dragStart$ = fromEventPattern(
(handler) => this.draggableInstance.on('drag:start', handler),
(handler) => this.draggableInstance.off('drag:start', handler)
);
dragStart$.subscribe(event => console.log('RxJS Drag Start', event));
}
Draggable поддерживает несколько опций, важных для UX:
handle — CSS-селектор для элемента, который будет
точкой захвата.draggable — селектор элементов, которые можно
перемещать.mirror — опции клона перемещаемого элемента
(constrainDimensions, appendTo).containers — массив DOM элементов для ограничения
области перетаскивания.this.draggableInstance = new Draggable(this.el.nativeElement, {
draggable: 'li',
handle: '.handle',
mirror: {
constrainDimensions: true
},
containers: [document.querySelector('.list-container')]
});
Dynamic content: Если элементы добавляются
динамически, использовать MutationObserver для
переинициализации Draggable или Sortable.
Change detection: Чтобы изменения позиции
элементов отражались в Angular, можно вручную обновлять модели через
события drag:stop или
sortable:sorted.
this.sortableInstance.on('sortable:sorted', (event) => {
const newIndex = event.newIndex;
const oldIndex = event.oldIndex;
this.items.splice(newIndex, 0, this.items.splice(oldIndex, 1)[0]);
});
Эти подходы обеспечивают гибкую и масштабируемую интеграцию Shopify Draggable в Angular-приложения с использованием директив, сохраняя архитектуру компонентов чистой и модульной.