Angular DropnDrop CDK结合响应式表单出现拖拽项重复异常
问题根因
三个核心错误导致数据重复异常:
- 表单类型定义错误:
myDivName初始化为普通FormControl,却在getter中强行断言为FormArray,初始值直接绑定整个源数据数组containerData,类型不匹配直接导致表单值同步逻辑混乱。 - 跨区拖拽重复写入数据:
transferArrayItem本身已经完成了源数组到目标数组的项移动,执行完该方法后又额外调用addItem向basket和表单中重复插入同一项,单次拖入操作实际写入了两次数据,直接触发倍增。 - 反向拖拽无同步移除逻辑:将项从目标区拖回源区域时,
transferArrayItem仅修改了展示用的数组数据,没有同步删除basket和FormArray中对应的项,残留数据直接导致重复。 - 同区域排序未同步表单顺序:目标区内拖拽排序只改了展示数组,没有同步调整
FormArray内元素顺序,提交时值顺序和实际展示不一致。
修复步骤
1. 修正表单初始化逻辑
将myDivName初始化为真正的FormArray实例,不要用普通FormControl绑定数组:
initTestForm() { this.myTestForm = this.fb.group({ myDivName: this.fb.array([]) // 初始为空,后续通过FormArray内置方法操作数据 }); }
2. 重写拖拽事件逻辑
移除transferArrayItem后多余的addItem调用,根据拖拽方向(拖入/拖出目标区、同区排序)同步操作表单数据,所有数据变更只执行一次:
drop(event: CdkDragDrop<Item[]>) { if (event.previousContainer === event.container) { // 同区域排序 moveItemInArray( event.container.data, event.previousIndex, event.currentIndex ); // 仅目标区排序需要同步FormArray顺序 if (event.container.id === 'target-basket') { const formArr = this.myDivName; const movedControl = formArr.at(event.previousIndex); formArr.removeAt(event.previousIndex); formArr.insert(event.currentIndex, movedControl); } } else { // 跨区域移动 transferArrayItem( event.previousContainer.data, event.container.data, event.previousIndex, event.currentIndex ); if (event.container.id === 'target-basket') { // 从源区拖入目标区:新增表单项 const dragItem = event.container.data[event.currentIndex]; this.basket.push(dragItem); this.myDivName.push(this.fb.control(dragItem)); } else { // 从目标区拖回源区:移除对应表单项 this.basket.splice(event.previousIndex, 1); this.myDivName.removeAt(event.previousIndex); } } }
3. 模板配置补充
给两个拖拽容器配置固定ID用于方向判断,避免对象引用判断失效:
<!-- 源列表容器 --> <div cdkDropList id="source-list" [cdkDropListData]="containerData" [cdkDropListConnectedTo]="['target-basket']"> <!-- 拖拽项内容 --> </div> <!-- 目标区容器 --> <div cdkDropList id="target-basket" [cdkDropListData]="basket" [cdkDropListConnectedTo]="['source-list']"> <!-- 拖拽项内容 --> </div>
验证说明
修复后所有拖拽操作只会对数据做单次变更:
- 单次拖入目标区不会重复插入数据
- 拖回源区会同步删除表单项,无残留
- 目标区内排序会同步更新表单值顺序
- 提交时直接取
this.myTestForm.value.myDivName即可拿到无重复、顺序和展示一致的提交数据,不需要额外做去重处理。
内容的提问来源于stack exchange,提问作者Flavio
相关产品推荐
相关产品推荐

