ng2-dragula嵌套拖拽异常:行拖拽正常但列拖拽失效报错
我来帮你分析下这个问题,之前我也遇到过类似的嵌套拖拽冲突情况。
首先看你原来的代码,确实能正常实现列在不同行之间拖拽:
<div> <row *ngFor="let row of rows" dragula="columns" [dragulaModel]="row?.columns"> <column *ngFor="let column of row?.columns"></column> </row> </div>
但给外层div加上行拖拽指令后,就出现了列拖拽失效的问题:
<div dragula="rows" [dragulaModel]="rows"> <row *ngFor="let row of rows" dragula="columns" [dragulaModel]="row?.columns"> <column *ngFor="let column of row?.columns"></column> </row> </div>
问题根源
核心原因是拖拽事件冒泡:外层的rows拖拽容器会捕获到内层columns容器的拖拽事件。当你尝试拖拽列时,外层的行拖拽逻辑会先响应,不仅干扰了列的拖拽行为,还会因为dragula错误地把列当成行来处理,导致DOM操作异常,也就是你看到的removeChild报错——因为列的父节点是行内的容器,而非外层的rows容器,dragula试图从错误的节点移除元素,自然就抛出了异常。
解决方案
1. 阻止拖拽事件冒泡
在组件中注入DragulaService,监听columns容器的拖拽开始事件,手动阻止事件向上冒泡,这样外层的行拖拽就不会干扰内层的列拖拽了:
import { DragulaService } from 'ng2-dragula'; constructor(private dragulaService: DragulaService) { // 针对columns容器,阻止拖拽事件冒泡 this.dragulaService.drag('columns') .subscribe(({ el }) => { // 阻止鼠标和触摸事件冒泡 el.addEventListener('mousedown', (e) => e.stopPropagation()); el.addEventListener('touchstart', (e) => e.stopPropagation()); }); }
2. 调整拖拽指令的绑定层级
要确保dragula="columns"指令绑定在直接包裹列的容器上,而不是<row>组件本身。如果你的<row>组件内部还有一层包裹div,一定要把列拖拽的指令移到这个内层容器上,这样dragula才能正确识别列的父节点:
<div dragula="rows" [dragulaModel]="rows"> <row *ngFor="let row of rows"> <!-- 将dragula指令移到row内部的列容器上 --> <div dragula="columns" [dragulaModel]="row?.columns"> <column *ngFor="let column of row?.columns"></column> </div> </row> </div>
3. 确认模型的可观察性
确保rows数组和每个row.columns数组在修改时能触发Angular的变更检测。如果是普通数组,直接修改后可以手动触发变更检测;如果用RxJS的可观察对象(比如BehaviorSubject),要确保订阅并正确更新视图。
额外提示
测试的时候可以先单独验证列拖拽是否正常,再加入行拖拽,逐步排查。另外,dragula的嵌套容器需要明确区分不同的容器名称(rows和columns),这一点你已经做对了,只要解决事件冒泡问题就可以恢复正常。
内容的提问来源于stack exchange,提问作者Chrillewoodz

