如何使垫表行拖放与 cdkDragHandle 一起使用,以便只能使用句柄拖动行?
Posted
技术标签:
【中文标题】如何使垫表行拖放与 cdkDragHandle 一起使用,以便只能使用句柄拖动行?【英文标题】:How to make a mat-table row drag-drop work with cdkDragHandle so that the row is only draggable using the handle? 【发布时间】:2019-07-28 10:13:50 【问题描述】:我发现thisstackblitz 示例使用角度 cdk 将拖放添加到垫表。但是,所需的行为是该行只能使用带有 cdkDragHandle 指令的元素拖动。在此示例中,您可以通过单击行上的任意位置来拖动元素。如何修改此行,以使该行只能使用拖动手柄拖动?
https://stackblitz.com/edit/angular-igmugp
【问题讨论】:
已回复***.com/questions/53307611/…。 【参考方案1】:恕我直言,除了破解/覆盖 Angular Material / CDK 的源代码之外,没有快速解决方法。这就是 github 上的开放功能请求:https://github.com/angular/material2/issues/13770。
问题是数据源/MatTable 上的 cdkDrag 会自动在所有子元素(生成行为)上创建拖动注释,并且不能(轻松)覆盖。
基于文档cdkDrag/cdkDragDisabled
- cdkDragHandle/cdkDragHandleDisabled
应该会有所帮助(它确实可以在没有表格的情况下工作)。我已经升级了示例中的所有库以支持它们,但没有效果。
【讨论】:
【参考方案2】:我通过将cdkDrag
应用于dragHandle 本身而不是行,并使用cdkDragRootElement
来识别行来实现该用户体验。它实现了通过手柄拖动的UX,但仍然存在一个错误,阻止了拖放后的实际重新排序。
See Stackblitz here.
Documentation of cdkDragRootElement is here.
Here is link to Github issue about it.
【讨论】:
你的例子破坏了排序功能。这是故意的吗? 从我写的内容中可以看出,实际重新排序仍然存在一个已知错误。我的所有解决方案都是允许通过拖动手柄拖动。不幸的是,这是一个不完整的解决方案,我们仍在等待 CDK 修复有缺陷的 Drag/Drop 实现。【参考方案3】:对于这个复杂的问题,我发现了一个稍微简单的问题。对于可拖动 tr 中的任何简单文本 td,我们可以使用 pointer-events:none ,它将禁用所有文本元素。
在手柄图标上,使用pointer-events:all,它将启用仅从图标拖动。
这也存在禁用所有锚点和按钮的问题。因此,对于图标和按钮,请执行以下操作
-
使用 mouseDown 设置标志
在拖动开始时,检查拖动并抛出 mouseup 事件
在拖动停止时,检查标志是否设置并重置标志并返回
检查这个stackblits以获得有效的答案 https://stackblitz.com/edit/angular-rwzc76
【讨论】:
我最终选择了一条不同的路线,只是有一个单独的重新排序视图,可以打开/关闭。但这对我来说似乎很好,很好! 我最终得到了两个不同的视图,一次只显示一个。因此,在页面顶部,我有一个打开“重新排序模式”的开关,当重新排序模式打开时,垫表被隐藏,并显示一个单独的可拖放项目列表。 类似的东西,但风格更好:stackblitz.com/edit/angular-pjg922 顺便说一句,我把我的名字改成了胡言乱语【参考方案4】:对于此处发布的解决方案,我有一个替代解决方案。除了需要更多交互性(包括文本选择、输入字段等)的链接和纯文本单元格之外,我还有一个要求。
对将在cdkDrag
表行(tr
)中呈现的任何不可拖动单元格使用指令,我能够阻止mousedown
事件冒泡到活动的cdkDrag
行cdkDropList
实例。
这是我的指令最终的样子。
import Directive, ElementRef, OnDestroy, OnInit from '@angular/core';
@Directive(
selector: '[appCancelCdkDrag]'
)
export class CancelCdkDrag implements OnInit, OnDestroy
$element: htmlElement;
constructor(el: ElementRef)
this.$element = el.nativeElement;
fireMouseUp($event: MouseEvent)
$event.cancelBubble = true;
ngOnDestroy(): void
this.$element.removeEventListener('mousedown', this.fireMouseUp);
ngOnInit(): void
this.$element.addEventListener('mousedown', this.fireMouseUp);
StackBlitz 在这里:https://stackblitz.com/edit/angular-tgrcni
这里是 Github Angular Components 页面上的相关评论:https://github.com/angular/components/issues/13770#issuecomment-553193486
希望这会有所帮助。
【讨论】:
【参考方案5】:这是我解决此问题的方法:
-
制作一个布尔值来控制是否应该禁用
cdkDrag
。默认行为已禁用。
将mousedown
、mouseup
、touchstart
和touchend
事件处理程序添加到cdkDragHandle
以切换控件。
在cdkDrag
中,监听cdkDragReleased
事件,将cdkDrag
拖拽后禁用。
副作用是使用您真正想要禁用的项目变得更加困难(例如,为那些真正禁用的项目应用样式)。
代码如下:
组件类 dragDisabled = true;
cdkDrag
<mat-row
*matRowDef="let row; columns: displayedColumns"
cdkDrag
[cdkDragData]="row"
[cdkDragDisabled]="dragDisabled"
(cdkDragReleased)="dragDisabled = true"
></mat-row>
cdkDragHandle
<mat-icon
cdkDragHandle
(touchstart)="dragDisabled = false"
(touchend)="dragDisabled = true"
(mousedown)="dragDisabled = false"
(mouseup)="dragDisabled = true"
>drag_indicator</mat-icon
>
【讨论】:
【参考方案6】:我在这里有一个替代答案(这在以前可能是不可能的) - 它结合了 @H Dog
和 @Mamoon ur Rasheed
的答案。
根据 H Dog 的回答,将拖动手柄移动到单元格本身而不是行中,并使用 cdkDragRootElement
选择父垫行。但是,这仍然会使整行保持可拖动状态。
接下来,默认禁用拖动,绑定到组件上的布尔值。当在拖动句柄上触发 mousedown 事件时,启用拖动,然后在下一帧中再次禁用它。
这会使整行保留为允许正常交互,但允许通过具有适当占位符和预览功能的拖动手柄进行拖动。
【讨论】:
【参考方案7】:这里是开始仅按特定列拖动行的示例:
在组件上创建变量
dragEnabled = false;
将行设置为可拖动并禁止通过变量拖动
<mat-row *matRowDef="let row; columns: columns;" cdkDrag [cdkDragDisabled]="!dragEnabled">
</mat-row>
通过特定列上的鼠标事件控制dragEnabled变量状态
<ng-container matColumnDef="drag">
<mat-header-cell *matHeaderCellDef>
</mat-header-cell>
<mat-cell *matCellDef="let entity"
(mouseenter)="dragEnabled = true"
(mouseleave)="dragEnabled = false">
<mat-icon>
drag_indicator
</mat-icon>
</mat-cell>
</ng-container>
现在您可以选择行内容并仅按特定列拖动行。
【讨论】:
完美且超级简单。很棒。以上是关于如何使垫表行拖放与 cdkDragHandle 一起使用,以便只能使用句柄拖动行?的主要内容,如果未能解决你的问题,请参考以下文章