如何创建可复用Angular组件,实现cdkDropList的外部引用?
重构Angular CDK拖拽列表为可复用组件:动态模板引用与跨实例关联方案
核心思路
将重复的拖拽列表封装为独立组件,通过输入属性传递配置、暴露组件内部的cdkDropList实例,由父组件统一管理所有列表的关联关系,彻底消除重复代码。
步骤1:创建可复用的Kanban列表组件
组件类(kanban-list.component.ts)
import { Component, Input, Output, EventEmitter, ViewChild } from '@angular/core'; import { CdkDropList, CdkDragDrop } from '@angular/cdk/drag-drop'; @Component({ selector: 'app-kanban-list', templateUrl: './kanban-list.component.html', styleUrls: ['./kanban-list.component.css'] }) export class KanbanListComponent { // 输入属性:列表标题、数据源、关联的拖拽列表集合 @Input() listTitle!: string; @Input() listItems!: any[]; @Input() connectedLists!: CdkDropList[]; // 输出事件:拖拽完成后向父组件传递事件对象 @Output() dropped = new EventEmitter<CdkDragDrop<any[]>>(); // 获取当前组件内部的cdkDropList实例,暴露给父组件调用 @ViewChild('dropList') dropList!: CdkDropList; // 保留原有的时间格式化逻辑 foregoneTime(time: number): string { const now = Date.now() / 1000; const diff = Math.floor(now - time); if (diff < 60) return `${diff}s`; if (diff < 3600) return `${Math.floor(diff/60)}m`; if (diff < 86400) return `${Math.floor(diff/3600)}h`; return `${Math.floor(diff/86400)}d`; } // 转发拖拽事件到父组件处理 drop(event: CdkDragDrop<any[]>) { this.dropped.emit(event); } }
组件模板(kanban-list.component.html)
<div class="example-container"> <h2>{{ listTitle }}</h2> <div class="example-list"> <div cdkDropList #dropList="cdkDropList" [cdkDropListData]="listItems" [cdkDropListConnectedTo]="connectedLists" (cdkDropListDropped)="drop($event)" > <div class="example-box" *ngFor="let item of listItems" cdkDrag> <div> <strong> <a target="_blank" [href]="item.url">{{item.title}}</a> </strong> <a *ngIf="item.hostname" class="hn" target="_blank" [href]="'https://news.ycombinator.com/from?site='+item.hostname" > ({{item.hostname}}) </a> </div> <div class="secondRow"> <span>by {{item.by}} </span> <span>Points: {{item.score}} </span> <span>{{foregoneTime(item.time)}} ago </span> </div> <div class="thirdRow"> <a target="_blank" [href]="item.url">Visit</a> <a class="hn" target="_blank" [href]="'https://news.ycombinator.com/item?id='+item.id" >{{item.descendants}} comments</a > </div> </div> </div> </div> </div>
步骤2:在父组件中使用可复用组件并管理关联关系
父组件类(drag-drop.component.ts)
import { Component, ViewChildren, QueryList, AfterViewInit } from '@angular/core'; import { CdkDragDrop, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop'; import { KanbanListComponent } from './kanban-list/kanban-list.component'; @Component({ selector: 'app-drag-drop', templateUrl: './drag-drop.component.html', styleUrls: ['./drag-drop.component.css'] }) export class DragDropComponent implements AfterViewInit { // 初始化各列表的数据源(替换为你的实际业务数据) newsItems = []; topItems = []; readLaterItems = []; watchItems = []; // 获取所有可复用列表组件的实例集合 @ViewChildren(KanbanListComponent) kanbanLists!: QueryList<KanbanListComponent>; ngAfterViewInit() { // 收集所有列表的cdkDropList实例,传递给每个列表作为关联目标 const allDropLists = this.kanbanLists.map(list => list.dropList); this.kanbanLists.forEach(list => { list.connectedLists = allDropLists; }); } // 统一处理所有拖拽事件 drop(event: CdkDragDrop<any[]>) { 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 ); } } }
父组件模板(drag-drop.component.html)
<app-kanban-list [listTitle]="'News 📰'" [listItems]="newsItems" (dropped)="drop($event)" ></app-kanban-list> <app-kanban-list [listTitle]="'Top 🚀'" [listItems]="topItems" (dropped)="drop($event)" ></app-kanban-list> <app-kanban-list [listTitle]="'Read Later 📚'" [listItems]="readLaterItems" (dropped)="drop($event)" ></app-kanban-list> <app-kanban-list [listTitle]="'Watch 🎥'" [listItems]="watchItems" (dropped)="drop($event)" ></app-kanban-list>
关键说明
- 动态模板引用管理:子组件通过
@ViewChild获取自身的cdkDropList实例,父组件用@ViewChildren批量收集所有子组件实例,无需手动定义多个不同的模板引用变量。 - 跨实例关联:在
ngAfterViewInit生命周期钩子中,将所有列表的dropList实例数组传递给每个子组件的connectedLists输入属性,实现所有列表之间的拖拽互通。 - 逻辑复用:拖拽事件统一在父组件处理,子组件仅负责渲染和事件转发,确保业务逻辑集中、代码可维护性提升。
内容的提问来源于stack exchange,提问作者user11092881
相关产品推荐
相关产品推荐

