Angular 4中ng2-dragula父子类别排序功能实现求助
Hey there! I’ve worked with ng2-dragula on similar nested sorting tasks, so let’s break down how to fix those wonky drag behaviors while keeping your parent-child structure intact (and functional).
Core Problem
The default ng2-dragula setup doesn’t distinguish between parent and child container hierarchies, leading to unexpected cross-level drags. We need to define explicit rules for which elements can be dragged where, and sync DOM changes back to your data model properly.
Step 1: Configure Separate Dragula "Bags"
First, set up distinct dragula containers (called "bags") for parent categories and child categories, with custom accepts logic to restrict valid drag targets.
In your component class:
import { DragulaService } from 'ng2-dragula'; constructor(private dragulaService: DragulaService) { // Configure parent category bag this.dragulaService.createGroup('parent-bag', { accepts: (el, target) => { // Only allow parent category elements to drop into parent containers return target.classList.contains('parent-container') && el.classList.contains('parent-category'); }, direction: 'vertical' // Keep sorting vertical }); // Configure child category bag this.dragulaService.createGroup('child-bag', { accepts: (el, target) => { // Allow child elements to drop into any child container (adjust if you need stricter rules) return target.classList.contains('child-container') && el.classList.contains('child-category'); }, direction: 'vertical' }); }
Step 2: Update Template Structure
Map the bags to your containers, and add data attributes to link DOM elements to your data model (critical for syncing later):
<!-- Parent Category Container --> <div class="parent-container" dragula="parent-bag"> <div class="parent-category" *ngFor="let parent of parentCategories; trackBy: trackParentById" [attr.data-parent-id]="parent.id" > {{ parent.name }} <!-- Child Category Container for this Parent --> <div class="child-container" dragula="child-bag"> <div class="child-category" *ngFor="let child of parent.children; trackBy: trackChildById" [attr.data-child-id]="child.id" > {{ child.name }} </div> </div> </div> </div>
Add trackBy functions to boost performance and avoid unnecessary re-renders:
trackParentById(index: number, parent: any): string { return parent.id; } trackChildById(index: number, child: any): string { return child.id; }
Step 3: Sync Drags to Your Data Model
Ng2-dragula doesn’t auto-update your array data—listen to drop events to reorder/transfer elements manually:
// Handle parent category reordering this.dragulaService.drop('parent-bag').subscribe(() => { this.updateParentOrder(); }); // Handle child category reordering and cross-parent transfers this.dragulaService.drop('child-bag').subscribe(({ el, source, target }) => { const sourceParent = source.closest('.parent-category'); const targetParent = target.closest('.parent-category'); // Get IDs to match data const childId = el.getAttribute('data-child-id'); const sourceParentId = sourceParent.getAttribute('data-parent-id'); const targetParentId = targetParent.getAttribute('data-parent-id'); // Find the child in the source parent's children const sourceParentData = this.parentCategories.find(p => p.id === sourceParentId); const childData = sourceParentData.children.find(c => c.id === childId); // Remove from source parent if it's a cross-parent drag if (sourceParentId !== targetParentId) { sourceParentData.children = sourceParentData.children.filter(c => c.id !== childId); } // Add to target parent and reorder const targetParentData = this.parentCategories.find(p => p.id === targetParentId); targetParentData.children = this.getSortedChildren(targetParent); }); // Helper to reorder parent categories based on DOM private updateParentOrder(): void { const parentElements = document.querySelectorAll('.parent-category'); this.parentCategories = Array.from(parentElements).map(el => { const parentId = el.getAttribute('data-parent-id'); return this.parentCategories.find(p => p.id === parentId); }); } // Helper to reorder child categories based on DOM private getSortedChildren(parentElement: Element): any[] { const childElements = parentElement.querySelectorAll('.child-category'); return Array.from(childElements).map(el => { const childId = el.getAttribute('data-child-id'); const parentId = parentElement.getAttribute('data-parent-id'); const parentData = this.parentCategories.find(p => p.id === parentId); return parentData.children.find(c => c.id === childId); }); }
Step 4: Clean Up Subscriptions
Avoid memory leaks by destroying the dragula bags when the component is removed:
ngOnDestroy(): void { this.dragulaService.destroy('parent-bag'); this.dragulaService.destroy('child-bag'); }
Key Notes
- Tweak the
acceptsfunction if you need to restrict child elements to specific parent containers (e.g., only allow dragging within the same parent). - Always use
trackBywith*ngForto prevent Angular from re-rendering all elements when the array changes. - Syncing DOM order back to your data model is non-negotiable—otherwise, your view and data will get out of sync.
This setup should eliminate those weird drag behaviors and let you sort parents and children smoothly!
内容的提问来源于stack exchange,提问作者lmarcelocc

