Reputation: 779
I found this stackblitz example of adding drag-drop to a mat-table using angular cdk. However, the desired behavior is that the row is only draggable using the element with the cdkDragHandle directive. In this example you can drag the element by clicking anywhere on the row. How can this be modified so that the row is only draggable using the drag handle?
https://stackblitz.com/edit/angular-igmugp
Upvotes: 10
Views: 15545
Reputation: 1894
Here an example to start to drag a row only by specific column:
Create a variable on component
dragEnabled = false;
Set the row as draggable and disable drag by the variable
<mat-row *matRowDef="let row; columns: columns;" cdkDrag [cdkDragDisabled]="!dragEnabled">
</mat-row>
Control the dragEnabled variable state by mouse events on specific column
<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>
Now you can select row contents and drag the row only by a specific column.
Upvotes: 2
Reputation: 174
I have an alternative answer here (which perhaps was not possible previously) - it combines @H Dog
and @Mamoon ur Rasheed
's answers.
As per H Dog's answer, move the drag handle into the cell itself instead of the row, and use cdkDragRootElement
to select the parent mat-row. However, this still leaves the full row as draggable.
Next, disable drag by default, bound to a boolean on the component. When a mousedown event is fired on the drag handle, enable drag and then, in the next frame, disable it again.
This leaves the full row as allowing normal interaction, but enables dragging via the drag handle with proper placeholder and preview functionality.
Upvotes: 1
Reputation: 476
Here is my workaround for this issue:
cdkDrag
should be disabled. Default behavior is disabled.mousedown
, mouseup
, touchstart
, and touchend
event handler to the cdkDragHandle
to toggle the control.cdkDrag
, listen to the cdkDragReleased
event to disable the cdkDrag
after it is dragged.The side-effect is that it becomes harder to work with items that you really want to disable (e.g. apply style for those truly disabled items).
The code looks like below:
dragDisabled = true;
<mat-row
*matRowDef="let row; columns: displayedColumns"
cdkDrag
[cdkDragData]="row"
[cdkDragDisabled]="dragDisabled"
(cdkDragReleased)="dragDisabled = true"
></mat-row>
<mat-icon
cdkDragHandle
(touchstart)="dragDisabled = false"
(touchend)="dragDisabled = true"
(mousedown)="dragDisabled = false"
(mouseup)="dragDisabled = true"
>drag_indicator</mat-icon
>
Upvotes: 16
Reputation: 1
I have an alternate solution to the ones posted here. I had a requirement beyond just links and plain text cells that required more interactivity (including text selection, input fields, etc).
Using a directive on any non-draggable cells that will be rendered in a cdkDrag
table row (tr
), I was able to stop the mousedown
event from bubbling down to the active cdkDrag
row of the cdkDropList
instance.
Here is what my directive eventually ended up looking like.
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 here: https://stackblitz.com/edit/angular-tgrcni
Here is a related comment on the Github Angular Components page: https://github.com/angular/components/issues/13770#issuecomment-553193486
Hope this helps.
Upvotes: 0
Reputation: 317
I have found a somewhat simple issue to this complex problem. For any simple text td in the draggable tr, we can use the pointer-events:none and it will disable all the text element.
On the handle icon, use the pointer-events:all and it will enable dragging from only the icon.
This also has the issue where it disables all the anchor and buttons. So for icon and buttons do the follwoing
check this stackblits for working answer https://stackblitz.com/edit/angular-rwzc76
Upvotes: 3
Reputation: 3785
I have achieved that UX by applying cdkDrag
to the dragHandle itself, instead of the row, and using cdkDragRootElement
to identify the row. It achieves the UX of dragging via handle, but there's still a bug that prevents the actual reordering after the drop.
Documentation of cdkDragRootElement is here.
Here is link to Github issue about it.
Upvotes: 5
Reputation: 1794
IMHO there is no quick-fix to this, other than hacking/overriding the source code of Angular Material / CDK. Testament of this is the open feature request at github: https://github.com/angular/material2/issues/13770.
The issue is that the cdkDrag on a datasource / MatTable automatically creates drag annotations on all child elements (which generates the behavior) and can't be (easily) overriden.
Based on the documentation cdkDrag/cdkDragDisabled
- cdkDragHandle/cdkDragHandleDisabled
should help (it does work without a table). I've upgraded all the libraries from the example to support them but to no effect.
Upvotes: 2