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

