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
相关产品推荐
相关产品推荐

