如何在Angular中实现支持自由拖拽放置的自定义看板?
Angular拖拽自由看板实现方案
Angular自带的DragDropModule完全支持你要的「右侧列表拖拽复制到左侧看板、看板内元素自由移动」的需求,你当前代码无法触发事件的核心原因是事件绑定语法错误、以及拖拽区域没有做关联配置,按以下步骤修改即可:
问题排查
你写的cdkDragEnded="dragEndedProcess()"缺少Angular事件绑定必需的括号,正确写法是(cdkDragEnded)="dragEndedProcess()",缺少括号的情况下框架不会把它识别为事件绑定,自然不会触发对应方法。
另外你没有关联左右两个拖拽区域,也没有对左侧看板做自由拖拽的适配,才会出现逻辑不生效的问题。
具体实现步骤
- 基础配置修正
- 右侧元素列表容器加
cdkDropList标识,配置[cdkDropListConnectedTo]关联左侧看板,开启cdkDropListCopyMode实现复制而非移动源元素 - 左侧看板容器同样加
cdkDropList标识,绑定(cdkDropListDropped)事件处理元素拖拽落下的逻辑
- 复制逻辑实现
- 拖拽结束时从事件对象中拿到携带的元素数据,直接新增到左侧看板的元素渲染数组中,不要操作右侧源数组即可实现复制效果
- 每个看板元素额外存储
left、top坐标属性,用于记录元素在看板内的位置
- 看板内自由移动适配
- 左侧看板设置
position: relative,内部的拖拽元素设置position: absolute,保证坐标相对于看板计算 - 看板内的元素绑定
(cdkDragEnded)事件,拖拽结束后更新元素自身的坐标属性,位置即可持久化
代码示例
模板代码
<!-- 右侧元素列表 --> <div cdkDropList #sourceList="cdkDropList" [cdkDropListData]="rightItemList" [cdkDropListConnectedTo]="['leftBoard']" cdkDropListCopyMode class="right-list" > <div class="rfid-box" cdkDrag *ngFor="let item of rightItemList" [cdkDragData]="item" > {{item.name}} </div> </div> <!-- 左侧看板 --> <div cdkDropList id="leftBoard" class="left-box" [cdkDropListData]="boardItemList" (cdkDropListDropped)="onItemDropped($event)" > <!-- 动态渲染拖拽生成的元素 --> <div class="board-item" cdkDrag cdkDragBoundary=".left-box" *ngFor="let item of boardItemList" [style.left.px]="item.left" [style.top.px]="item.top" (cdkDragEnded)="onBoardItemDragEnd($event, item)" > {{item.name}} </div> </div>
组件逻辑代码
import { CdkDragDrop, moveItemInArray, CdkDragEnd } from '@angular/cdk/drag-drop'; // 组件类内部 rightItemList = [ {id: 1, name: 'Télécommande'}, // 其他右侧列表元素 ]; boardItemList = []; // 元素从右侧拖拽到左侧看板触发 onItemDropped(event: CdkDragDrop<any[]>) { // 只处理从右侧列表拖过来的元素 if (event.previousContainer !== event.container) { // 复制元素到看板,初始化坐标为落下的位置 const newItem = {...event.previousContainer.data[event.previousIndex]}; newItem.left = event.dropPoint.x - event.container.element.nativeElement.offsetLeft; newItem.top = event.dropPoint.y - event.container.element.nativeElement.offsetTop; this.boardItemList.push(newItem); } } // 看板内元素拖拽结束更新坐标 onBoardItemDragEnd(event: CdkDragEnd, item: any) { const position = event.source.getFreeDragPosition(); item.left = position.x; item.top = position.y; }
样式代码
.left-box { position: relative; width: 70%; height: 100vh; border: 1px solid #eee; } .right-list { width: 30%; padding: 16px; border-left: 1px solid #eee; } .board-item { position: absolute; padding: 8px 12px; background: #fff; border: 1px solid #ccc; cursor: move; } .rfid-box { padding: 8px 12px; margin-bottom: 8px; background: #f5f5f5; cursor: move; }
效果示例
内容的提问来源于stack exchange,提问作者Matt
相关产品推荐
相关产品推荐

