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

如何在SideNav与Test组件间实现列表元素的跨组件拖拽?

实现Angular Material跨组件拖拽(SideNav ↔ Test)

其实跨组件拖拽并没有想象中复杂,Material的DragDropModule本身就支持这个场景,只是文档没重点展开而已。核心就是让两个组件的拖拽列表建立连接,再通过共享数据源同步两边的列表数据。下面是具体的实现步骤和代码示例:

1. 基础准备:导入DragDropModule

不管SideNav还是Test组件所在的模块,都要先导入DragDropModule:

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

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

2. 创建共享数据服务(核心!)

因为两个组件是独立的,必须用一个服务来维护两边的拖拽数据源,确保拖拽操作后两边的数据能同步更新:

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';

@Injectable({ providedIn: 'root' })
export class DragDropService {
  // 维护SideNav的元素列表
  private sideNavItemsSubject = new BehaviorSubject<any[]>([
    { id: 1, name: '侧边栏元素1' },
    { id: 2, name: '侧边栏元素2' }
  ]);
  sideNavItems$ = this.sideNavItemsSubject.asObservable();

  // 维护Test组件的元素列表
  private testItemsSubject = new BehaviorSubject<any[]>([
    { id: 3, name: '测试元素A' }
  ]);
  testItems$ = this.testItemsSubject.asObservable();

  // 更新SideNav列表
  updateSideNavItems(items: any[]) {
    this.sideNavItemsSubject.next([...items]);
  }

  // 更新Test列表
  updateTestItems(items: any[]) {
    this.testItemsSubject.next([...items]);
  }
}

3. SideNav组件实现

在SideNav的模板里,给拖拽列表设置唯一ID,并指定要连接的Test组件列表ID,同时绑定服务的数据源:

<!-- side-nav.component.html -->
<div cdkDropList 
     id="side-nav-drop-list"
     [cdkDropListConnectedTo]="['test-drop-list']"
     [cdkDropListData]="sideNavItems"
     (cdkDropListDropped)="onDrop($event)">
  <div *ngFor="let item of sideNavItems" cdkDrag [cdkDragData]="item">
    {{ item.name }}
  </div>
</div>

然后在组件类里注入服务,订阅数据并处理拖拽事件:

// side-nav.component.ts
import { Component, OnInit } from '@angular/core';
import { CdkDragDrop, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop';
import { DragDropService } from '../drag-drop.service';

@Component({
  selector: 'app-side-nav',
  templateUrl: './side-nav.component.html'
})
export class SideNavComponent implements OnInit {
  sideNavItems: any[] = [];

  constructor(private dragDropService: DragDropService) {}

  ngOnInit() {
    this.dragDropService.sideNavItems$.subscribe(items => {
      this.sideNavItems = items;
    });
  }

  onDrop(event: CdkDragDrop<any[]>) {
    // 同一个列表内拖拽,直接调整元素位置
    if (event.previousContainer === event.container) {
      moveItemInArray(this.sideNavItems, event.previousIndex, event.currentIndex);
      this.dragDropService.updateSideNavItems(this.sideNavItems);
    } else {
      // 跨组件拖拽,转移元素到目标列表
      transferArrayItem(
        event.previousContainer.data,
        event.container.data,
        event.previousIndex,
        event.currentIndex
      );
      // 同步更新两边的数据源
      this.dragDropService.updateSideNavItems(this.sideNavItems);
      this.dragDropService.updateTestItems(event.container.data);
    }
  }
}

4. Test组件实现

和SideNav组件逻辑一致,模板里设置对应ID并连接到SideNav的列表:

<!-- test.component.html -->
<div cdkDropList 
     id="test-drop-list"
     [cdkDropListConnectedTo]="['side-nav-drop-list']"
     [cdkDropListData]="testItems"
     (cdkDropListDropped)="onDrop($event)">
  <div *ngFor="let item of testItems" cdkDrag [cdkDragData]="item">
    {{ item.name }}
  </div>
</div>

组件类里注入服务,处理拖拽事件:

// test.component.ts
import { Component, OnInit } from '@angular/core';
import { CdkDragDrop, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop';
import { DragDropService } from '../drag-drop.service';

@Component({
  selector: 'app-test',
  templateUrl: './test.component.html'
})
export class TestComponent implements OnInit {
  testItems: any[] = [];

  constructor(private dragDropService: DragDropService) {}

  ngOnInit() {
    this.dragDropService.testItems$.subscribe(items => {
      this.testItems = items;
    });
  }

  onDrop(event: CdkDragDrop<any[]>) {
    if (event.previousContainer === event.container) {
      moveItemInArray(this.testItems, event.previousIndex, event.currentIndex);
      this.dragDropService.updateTestItems(this.testItems);
    } else {
      transferArrayItem(
        event.previousContainer.data,
        event.container.data,
        event.previousIndex,
        event.currentIndex
      );
      this.dragDropService.updateTestItems(this.testItems);
      this.dragDropService.updateSideNavItems(event.container.data);
    }
  }
}

5. 关键注意点

  • cdkDropListConnectedTo必须设置两边的列表ID,这是跨组件拖拽能生效的核心
  • 必须用共享服务维护数据,因为两个组件独立,直接操作各自数组无法同步
  • 更新数组时要创建新数组(用[...items]),这样BehaviorSubject才能触发订阅更新视图
  • 如果需要自定义拖拽预览样式,可以用cdkDragPreview指令修改

这样就能实现SideNav和Test组件之间的双向拖拽了,亲测有效!

内容的提问来源于stack exchange,提问作者Sanket Salunkhe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 03:55:32