Angular Mat Table树形列表拖拽:占位符位置适配边界问题
解决Mat Table树形列表拖拽的占位符位置与指示器隐藏问题
一、修复占位符位置错误问题
核心思路是根据鼠标在目标行的垂直位置,动态判断占位符的显示位置(顶部/底部),同时区分中间区域的嵌套逻辑:
- 获取鼠标与目标行的相对位置
在拖拽进入事件中,计算鼠标相对于目标行的垂直偏移量,结合行高判断所处区域:
import { CdkDrag, CdkDropList } from '@angular/cdk/drag-drop'; // 存储当前插入位置的状态 currentInsertPosition: 'top' | 'bottom' | 'nested' = 'top'; // 自定义拖拽进入判断逻辑 dropListEnterPredicate = (drag: CdkDrag, drop: CdkDropList) => { const targetRow = drop.element.nativeElement; const rowRect = targetRow.getBoundingClientRect(); const mouseRelativeY = drag.dropPoint.y - rowRect.top; const rowHeight = rowRect.height; // 设置25%阈值划分区域:顶部25%、底部25%、中间50% if (mouseRelativeY < rowHeight * 0.25) { this.currentInsertPosition = 'top'; } else if (mouseRelativeY > rowHeight * 0.75) { this.currentInsertPosition = 'bottom'; } else { this.currentInsertPosition = 'nested'; } return true; };
- 动态控制占位符位置
在模板中根据currentInsertPosition给占位符添加样式类,控制其显示位置:
<!-- 拖拽占位符模板 --> <ng-template cdkDragPlaceholder> <div class="drag-placeholder" *ngIf="currentInsertPosition !== 'nested'" [class.top]="currentInsertPosition === 'top'" [class.bottom]="currentInsertPosition === 'bottom'"> </div> </ng-template>
- 添加占位符样式
通过CSS确保占位符在顶部/底部的正确显示:
.drag-placeholder { height: 2px; background-color: #2196F3; margin: 4px 0; } .drag-placeholder.top { margin-bottom: 8px; /* 与上方行保持间距 */ } .drag-placeholder.bottom { margin-top: 8px; /* 与下方行保持间距 */ }
二、正确隐藏移动指示器(避免破坏UI)
不要使用[hidden]指令,它通过display: none隐藏元素会打乱布局,推荐两种方案:
方案1:用*ngIf直接移除占位符
当处于中间嵌套区域时,直接不渲染占位符:
<div class="drag-placeholder" *ngIf="currentInsertPosition !== 'nested'" [class.top]="currentInsertPosition === 'top'" [class.bottom]="currentInsertPosition === 'bottom'"> </div>
方案2:用CSS类控制可见性与占位空间
如果需要保留元素位置(避免布局跳动),用visibility: hidden和尺寸重置:
.drag-placeholder.hidden { visibility: hidden; height: 0; margin: 0; }
模板中动态添加类:
<div class="drag-placeholder" [class.hidden]="currentInsertPosition === 'nested'" [class.top]="currentInsertPosition === 'top'" [class.bottom]="currentInsertPosition === 'bottom'"> </div>
三、嵌套逻辑配合
当currentInsertPosition为nested时,在拖拽完成的drop事件中,将拖拽项添加到目标行的子节点数组中,而非调整同级位置:
handleDrop(event: CdkDragDrop<any[]>) { if (this.currentInsertPosition === 'nested') { // 获取目标行数据,将拖拽项添加到其子节点 const targetItem = event.container.data[event.currentIndex]; targetItem.children = [...(targetItem.children || []), event.item.data]; } else { // 处理同级移动逻辑,根据currentInsertPosition调整插入索引 const insertIndex = this.currentInsertPosition === 'bottom' ? event.currentIndex + 1 : event.currentIndex; moveItemInArray(event.container.data, event.previousIndex, insertIndex); } }
内容的提问来源于stack exchange,提问作者Marcel Lorenz
相关产品推荐
相关产品推荐

