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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 03:12:23