You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular 4中ng2-dragula父子类别排序功能实现求助

Fixing ng2-dragula Parent-Child Category Sorting Issues

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 accepts function if you need to restrict child elements to specific parent containers (e.g., only allow dragging within the same parent).
  • Always use trackBy with *ngFor to 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 09:30:23