如何使 mat-table 行拖放与 cdkDragHandle 一起工作,以便只能使用句柄拖动该行?
How to make a mat-table row drag-drop work with cdkDragHandle so that the row is only draggable using the handle?
我找到了 this stackblitz 使用 angular cdk 添加拖放到 mat-table 的示例。但是,所需的行为是该行只能使用带有 cdkDragHandle 指令的元素进行拖动。在此示例中,您可以通过单击行上的任意位置来拖动元素。如何对此进行修改,以便只能使用拖动手柄拖动该行?
恕我直言,除了 hacking/overriding Angular Material / CDK 的源代码外,没有对此的快速修复。 github: https://github.com/angular/material2/issues/13770 的开放功能请求证明了这一点。
问题是数据源/MatTable 上的 cdkDrag 会自动在所有子元素(生成行为)上创建拖动注释并且不能(轻易)覆盖。
根据文档 cdkDrag/cdkDragDisabled
- cdkDragHandle/cdkDragHandleDisabled
应该有所帮助(它在没有 table 的情况下也能正常工作)。我已经升级了示例中的所有库以支持它们,但没有效果。
我通过将 cdkDrag
应用于 dragHandle 本身而不是行,并使用 cdkDragRootElement
来标识行来实现该 UX。它实现了通过句柄拖动的用户体验,但仍然存在一个错误,阻止了拖放后的实际重新排序。
对于这个复杂的问题,我发现了一个稍微简单的问题。对于可拖动 tr 中的任何简单文本 td,我们可以使用 pointer-events:none 它将禁用所有文本元素。
在手柄图标上,使用 pointer-events:all 它将启用仅从图标拖动。
这也有禁用所有锚点和按钮的问题。因此,对于图标和按钮,请执行以下操作
- 使用 mouseDown 设置标志
- 开始拖动时,检查拖放鼠标弹起事件
- 拖动停止时,检查标志是否已设置并重置标志 return
检查这个 stackblits 的工作答案
https://stackblitz.com/edit/angular-rwzc76
对于此处发布的解决方案,我有一个替代解决方案。我的要求不仅仅是链接和需要更多交互性的纯文本单元格(包括文本选择、输入字段等)。
对将在 cdkDrag
table 行 (tr
) 中呈现的任何不可拖动的单元格使用指令,我能够停止 mousedown
事件从向下冒泡到 cdkDropList
实例的活动 cdkDrag
行。
这是我的指令最终的样子。
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 组件页面上的相关评论:https://github.com/angular/components/issues/13770#issuecomment-553193486
希望对您有所帮助。
这是我解决此问题的方法:
- 做一个布尔值来控制是否应该禁用
cdkDrag
。默认行为已禁用。
- 将
mousedown
、mouseup
、touchstart
和 touchend
事件处理程序添加到 cdkDragHandle
以切换控件。
- 在
cdkDrag
中,监听cdkDragReleased
事件,使cdkDrag
拖动后失效
副作用是使用您真正想要禁用的项目变得更加困难(例如,为那些真正禁用的项目应用样式)。
代码如下所示:
- 组件class
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
>
我在这里有一个替代答案(以前可能不可能)- 它结合了 @H Dog
和 @Mamoon ur Rasheed
的答案。
根据 H Dog 的回答,将拖动手柄移动到单元格本身而不是行中,并使用 cdkDragRootElement
到 select 父垫行。但是,这仍然使整行保持可拖动状态。
接下来,默认禁用拖动,绑定到组件上的布尔值。当在拖动手柄上触发 mousedown 事件时,启用拖动,然后在下一帧中再次禁用它。
这会保留整行以允许正常交互,但允许通过具有适当占位符和预览功能的拖动手柄进行拖动。
这里是一个开始只按特定列拖动行的例子:
在组件上创建一个变量
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>
现在您可以 select 行内容并将行拖动到特定列。
我找到了 this stackblitz 使用 angular cdk 添加拖放到 mat-table 的示例。但是,所需的行为是该行只能使用带有 cdkDragHandle 指令的元素进行拖动。在此示例中,您可以通过单击行上的任意位置来拖动元素。如何对此进行修改,以便只能使用拖动手柄拖动该行?
恕我直言,除了 hacking/overriding Angular Material / CDK 的源代码外,没有对此的快速修复。 github: https://github.com/angular/material2/issues/13770 的开放功能请求证明了这一点。
问题是数据源/MatTable 上的 cdkDrag 会自动在所有子元素(生成行为)上创建拖动注释并且不能(轻易)覆盖。
根据文档 cdkDrag/cdkDragDisabled
- cdkDragHandle/cdkDragHandleDisabled
应该有所帮助(它在没有 table 的情况下也能正常工作)。我已经升级了示例中的所有库以支持它们,但没有效果。
我通过将 cdkDrag
应用于 dragHandle 本身而不是行,并使用 cdkDragRootElement
来标识行来实现该 UX。它实现了通过句柄拖动的用户体验,但仍然存在一个错误,阻止了拖放后的实际重新排序。
对于这个复杂的问题,我发现了一个稍微简单的问题。对于可拖动 tr 中的任何简单文本 td,我们可以使用 pointer-events:none 它将禁用所有文本元素。
在手柄图标上,使用 pointer-events:all 它将启用仅从图标拖动。
这也有禁用所有锚点和按钮的问题。因此,对于图标和按钮,请执行以下操作
- 使用 mouseDown 设置标志
- 开始拖动时,检查拖放鼠标弹起事件
- 拖动停止时,检查标志是否已设置并重置标志 return
检查这个 stackblits 的工作答案 https://stackblitz.com/edit/angular-rwzc76
对于此处发布的解决方案,我有一个替代解决方案。我的要求不仅仅是链接和需要更多交互性的纯文本单元格(包括文本选择、输入字段等)。
对将在 cdkDrag
table 行 (tr
) 中呈现的任何不可拖动的单元格使用指令,我能够停止 mousedown
事件从向下冒泡到 cdkDropList
实例的活动 cdkDrag
行。
这是我的指令最终的样子。
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 组件页面上的相关评论:https://github.com/angular/components/issues/13770#issuecomment-553193486
希望对您有所帮助。
这是我解决此问题的方法:
- 做一个布尔值来控制是否应该禁用
cdkDrag
。默认行为已禁用。 - 将
mousedown
、mouseup
、touchstart
和touchend
事件处理程序添加到cdkDragHandle
以切换控件。 - 在
cdkDrag
中,监听cdkDragReleased
事件,使cdkDrag
拖动后失效
副作用是使用您真正想要禁用的项目变得更加困难(例如,为那些真正禁用的项目应用样式)。
代码如下所示:
- 组件class
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
>
我在这里有一个替代答案(以前可能不可能)- 它结合了 @H Dog
和 @Mamoon ur Rasheed
的答案。
根据 H Dog 的回答,将拖动手柄移动到单元格本身而不是行中,并使用 cdkDragRootElement
到 select 父垫行。但是,这仍然使整行保持可拖动状态。
接下来,默认禁用拖动,绑定到组件上的布尔值。当在拖动手柄上触发 mousedown 事件时,启用拖动,然后在下一帧中再次禁用它。
这会保留整行以允许正常交互,但允许通过具有适当占位符和预览功能的拖动手柄进行拖动。
这里是一个开始只按特定列拖动行的例子:
在组件上创建一个变量
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>
现在您可以 select 行内容并将行拖动到特定列。