使用 Angular CDK 实现父子元素拖拽列表

本文旨在指导开发者使用 Angular CDK 创建一个支持父子元素拖拽的列表。我们将详细介绍如何配置 cdkDropList 和 cdkDrag 指令,以及如何处理拖拽事件,实现父元素和子元素之间的自由拖拽。通过本文,你将能够构建一个灵活且可交互的拖拽列表组件。

实现父子元素拖拽列表

Angular CDK (Component Dev Kit) 提供了一套强大的工具,用于构建自定义 UI 组件,其中包括拖拽功能。 要实现一个支持父子元素拖拽的列表,我们需要正确配置 cdkDropList 和 cdkDrag 指令,并处理相应的拖拽事件。

准备工作

确保你已经安装了 Angular CDK。如果没有,可以使用以下命令安装:

npm install @angular/cdk

同时,需要在你的 Angular 模块中导入 DragDropModule:

import { DragDropModule } from '@angular/cdk/drag-drop';

@NgModule({
  imports: [
    // ...
    DragDropModule,
  ],
  // ...
})
export class AppModule { }

HTML 模板

以下是一个基本的 HTML 模板结构,用于展示父子元素的拖拽列表:

  
  • {{item.name}}
    • {{subItem.name}}

关键点:

  • cdkDropListGroup: 将整个列表包裹在 cdkDropListGroup 中,允许在不同的 cdkDropList 之间拖拽。
  • cdkDropList: 应用于父列表和子列表的 ul 元素,使其成为拖拽容器。
  • cdkDropListData: 绑定到父列表和子列表的数据。
  • cdkDrag: 应用于父列表和子列表的 li 元素,使其成为可拖拽的元素。
  • (cdkDropListDropped): 监听拖拽完成事件,并调用 drop() 方法处理拖拽逻辑。
  • 移除 [cdkDropListConnectedTo]="parentIds": 这是解决子元素无法在不同父元素之间拖拽的关键。 cdkDropListConnectedTo 限制了拖拽的目标容器,移除后,子元素可以自由拖拽到任何 cdkDropList 容器中。

组件代码 (TypeScript)

以下是对应的 TypeScript 代码:

import { Component } from '@angular/core';
import { CdkDragDrop, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop';

@Component({
  selector: 'app-drag-drop-list',
  templateUrl: './drag-drop-list.component.html',
  styleUrls: ['./drag-drop-list.component.css']
})
export class DragDropListComponent {
  parentList: any[] = [
    { id: 1, name: 'Parent 1', children: [{ id: 11, name: 'Child 1' }, { id: 12, name: 'Child 2' }] },
    { id: 2, name: 'Parent 2', children: [{ id: 21, name: 'Child 3' }, { id: 22, name: 'Child 4' }] }
  ];

  drop(event: CdkDragDrop) {
    if (event.previousContainer === event.container) {
      moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
    } else {
      transferArrayItem(
        event.previousContainer.data,
        event.container.data,
        event.previousIndex,
        event.currentIndex
      );
    }
  }
}

代码解释:

  • parentList: 包含父元素和子元素的数组。
  • drop(event: CdkDragDrop): 处理拖拽事件的函数。
    • event.previousContainer:拖拽开始的容器。
    • event.container:拖拽结束的容器。
    • event.previousIndex:拖拽元素的原始索引。
    • event.currentIndex:拖拽元素的目标索引。
    • moveItemInArray:如果元素在同一个容器内移动,则使用此函数更新数组。
    • transferArrayItem:如果元素在不同的容器之间移动,则使用此函数更新数组。

CSS 样式 (可选)

为了更好地展示拖拽效果,可以添加一些 CSS 样式:

.example-list {
  border: solid 1px #ccc;
  min-height: 60px;
  background: white;
  border-radius: 4px;
  overflow: hidden;
  display: block;
}

.example-list li {
  padding: 10px;
  border-bottom: solid 1px #ccc;
  color: rgba(0, 0, 0, 0.87);
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  box-sizing: border-box;
  cursor: move;
  background: white;
  font-size: 14px;
}

.example-list li:last-child {
  border: none;
}

.example-list li cdk-drag-preview {
  box-sizing: border-box;
  border-radius: 4px;
  box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),
              0 8px 10px 1px rgba(0, 0, 0, 0.14),
              0 3px 14px 2px rgba(0, 0, 0, 0.12);
}

.example-list li cdk-drag-placeholder {
  opacity: 0;
}

.example-list li:active {
  cursor: gra*g;
}

.example-list li:hover {
    background-color: #f5f5f5;
}

ul {
    list-style-type: none;
    padding: 0;
}

总结

通过以上步骤,你就可以使用 Angular CDK 实现一个支持父子元素自由拖拽的列表。 核心在于正确配置 cdkDropList 和 cdkDrag 指令,并处理 drop 事件来更新数据模型。 记住,移除 [cdkDropListConnectedTo] 是实现子元素在不同父元素之间拖拽的关键。 实际应用中,可以根据具体需求调整代码,例如添加拖拽占位符、自定义拖拽预览等。