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

Angular嵌套拖拽:悬停时将L2项追加到L1项子列表

Angular CDK嵌套拖拽:L2项拖拽至L1项的实现方案

调整模板结构

修改原有模板,通过cdkDropListGroup管理拖拽组,给每个L1项配置独立的拖拽接收容器,并关联L2的拖拽列表:

<!-- L1 容器组 -->
<div cdkDropListGroup>
  <!-- 每个L1项作为拖拽目标,接收L2项 -->
  <div *ngFor="let l1Item of L1Items" 
       cdkDropList 
       [cdkDropListData]="l1Item"
       [cdkDropListConnectedTo]="[l2DropList]"
       (cdkDropListDropped)="onDropToL1($event)">
    <div cdkDrag>
      <!-- L1 内容展示 -->
      {{ l1Item.name }}
      <!-- 可选:渲染L1的子列表 -->
      <div *ngIf="l1Item.l2Items?.length" class="l2-sub-list">
        <div *ngFor="let child of l1Item.l2Items" class="l2-item">
          {{ child.name }}
        </div>
      </div>
    </div>
  </div>
</div>

<!-- L2 拖拽容器 -->
<div #l2DropList cdkDropList [cdkDropListData]="L2Items" (cdkDropListDropped)="onL2ItemDropped($event)">
  <div *ngFor="let l2Item of L2Items" cdkDrag class="l2-draggable">
    <!-- L2 内容展示 -->
    {{ l2Item.name }}
  </div>
</div>

组件逻辑实现

在组件类中定义数据结构,处理拖拽事件,实现判断逻辑和数据更新:

1. 定义数据接口(规范结构)

interface L1Item {
  id: number;
  name: string;
  l2Items?: L2Item[]; // 存储子列表的字段
}

interface L2Item {
  id: number;
  name: string;
}

2. 核心逻辑代码

import { Component, ViewChild } from '@angular/core';
import { CdkDragDrop, CdkDropList } from '@angular/cdk/drag-drop';
import { MatSnackBar } from '@angular/material/snack-bar'; // 用Material提示组件,也可替换为alert

@Component({
  selector: 'app-nested-drag',
  templateUrl: './nested-drag.component.html',
  styleUrls: ['./nested-drag.component.css']
})
export class NestedDragComponent {
  @ViewChild('l2DropList') l2DropList!: CdkDropList;

  // 模拟初始数据
  L1Items: L1Item[] = [
    { id: 1, name: 'L1-1', l2Items: [] },
    { id: 2, name: 'L1-2' }, // 无子列表的项
    { id: 3, name: 'L1-3', l2Items: [] }
  ];

  L2Items: L2Item[] = [
    { id: 1, name: 'L2-1' },
    { id: 2, name: 'L2-2' }
  ];

  constructor(private snackBar: MatSnackBar) {}

  // 处理L2项拖到L1项的逻辑
  onDropToL1(event: CdkDragDrop<L1Item, L2Item>) {
    const draggedItem = event.item.data; // 获取拖拽的L2项
    const targetL1 = event.container.data; // 获取目标L1项

    // 判断L1项是否有子列表
    if (!targetL1.l2Items) {
      // 弹出错误提示
      this.snackBar.open('该L1项无可用子列表,无法添加L2项', '关闭', { duration: 3000 });
      return;
    }

    // 将L2项追加到L1子列表,并从原L2列表移除
    targetL1.l2Items.push(draggedItem);
    this.L2Items = this.L2Items.filter(item => item.id !== draggedItem.id);
  }

  // 处理L2内部拖拽排序(可选)
  onL2ItemDropped(event: CdkDragDrop<L2Item>) {
    const [movedItem] = event.container.data.splice(event.previousIndex, 1);
    event.container.data.splice(event.currentIndex, 0, movedItem);
  }
}

关键配置说明

  • 拖拽关联:通过[cdkDropListConnectedTo]将L1的每个接收容器与L2的拖拽容器绑定,确保L2项可以跨列表拖拽。
  • 数据绑定:cdkDropListData用来传递容器对应的业务数据,拖拽事件中可直接获取目标和拖拽项的原始数据。
  • 错误提示:示例用MatSnackBar实现友好提示,若未使用Material组件库,可替换为原生alert()或自定义提示组件。
  • 数据一致性:添加L2项到L1子列表后,必须从原L2列表中移除,避免数据重复。

必备模块导入

确保在NgModule中导入所需模块:

import { DragDropModule } from '@angular/cdk/drag-drop';
import { MatSnackBarModule } from '@angular/material/snack-bar'; // 若用SnackBar则导入

@NgModule({
  imports: [
    // ...其他模块
    DragDropModule,
    MatSnackBarModule
  ]
})
export class AppModule { }

内容的提问来源于stack exchange,提问作者Shivvangi Sharma

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 20:25:23