如何使垫表行拖放与 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 事件冒泡到活动的cdkDragcdkDropList 实例。

这是我的指令最终的样子。

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。默认行为已禁用。 将mousedownmouseuptouchstarttouchend 事件处理程序添加到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 一起使用,以便只能使用句柄拖动行?的主要内容,如果未能解决你的问题,请参考以下文章

拖放与物理行为

Angular 6 - 路由未发生 - 垫表行

如何使用 jQuery 拖放与段落元素

如何让 jQueryUI 拖放与触摸设备一起使用

拖放与视觉指示

nstableview 拖放与自定义单元格视图