18

Using @angular/cdk 7.2.1: If defining a parent component holding a cdkDropList and a nested list of cdkDrag components, defining a cdkDragHandle inside the nested child component doesn't work. If the same structure is all in the same component, cdkDragHandle works perfectly.

https://stackblitz.com/edit/angular-wfvmuj?embed=1&file=src/app/hello.component.html

Has anyone found a fix to get cdkDragHandle to work even when not defined in the same component as cdkDrag?

Simon
  • 2,994
  • 3
  • 28
  • 37

5 Answers5

30

This solution worked for me:

Parent Component:

<div cdkDropList #list="cdkDropList"
  [cdkDropListData]="myArray"
  (cdkDropListDropped)="drop($event)">

  <app-item
    *ngFor="let item of myArray"
    cdkDrag>
    <div cdkDragHandle></div>
  </app-item>

</div>

Child Component (app-item):

<div class="drag-container">
  <ng-content></ng-content>
  <div class="drag-handle">drag here</div>
</div>

Then style the cdk-drag-handle class in the parent component. cdk-drag-handle comes with material, so we do not need to apply it manually:

.cdk-drag-handle {
   width: 100%;
   height: 100%;
   position: absolute;
   z-index: 100;
   background-color: transparent;
   cursor: move;
 }

Then style the drag-container with position: relative and whatever you want. I have an item inside it (drag-handle) which also takes the full width and height of the container, that contains an image (just as a sidenote).

lajuma
  • 406
  • 5
  • 9
  • 2
    Thank you, lajuma, for the proposed solution. This is basically what is suggested by others in the referenced issue link above, github.com/angular/material2/issues/13784. – Simon Jul 25 '19 at 15:39
  • Great solution! You just introduced me to tag. Thanks – Daniel Székely Jul 12 '22 at 21:59
9

This worked for me: Instead of using cdkDragHandle, just stop mouse down event propagation as bellow. Then only header can be dragged.

<div>
  <header></header>
  <body (mousedown)="$event.stopPropagation()"></body>
</div>
AllenSS
  • 91
  • 1
  • 2
4

Not sure when this was added , setting cdkDragRootElement in the child worked for me.

In the child component <div cdkDrag cdkDragRootElement="app-bot-prompt-selector"> app-bot-prompt-selector is the parent element that needs to be drag able

https://material.angular.io/cdk/drag-drop/api#CdkDrag

Exlord
  • 5,009
  • 4
  • 31
  • 51
0

Because CdkDrag use @ContentChildren to catch all CdkDragHandles, so you have to define all CdkDragHandles as content children BUT there is another solution if you don't want to define cdkDragHandle as descendants content children, you can add cdkDragHandle to cdkDrag manually:

@Component({
  selector: "child-component",
  template: `
    <div cdkDragHandle>handle</div>
  `
)}
export class ChildComponent {

  @ViewChild(CdkDragHandle, { static: true }) handle: CdkDragHandle;

  constructor(@Optional() @Inject(CDK_DRAG_PARENT) public cdk) {}

  ngAfterViewInit() {
    this.cdk._handles.length = 1;
    this.cdk._handles._results = [this.handle];
    this.cdk._handles.changes.next();
  }
}
Mehdi Shakeri
  • 584
  • 3
  • 11
0

A solution I found to this problem is to add the drag handle as a content but not the main one, because I needed that for other components.

  
<div cdkDropListGroup>
<div 
#mainContainerList="cdkDropList" 
cdkDropList 
(cdkDropListDropped)="drop($event)"  
[cdkDropListData]="this.dataModel.model.items"

class="page-builder-content">
    <div cdkDrag *ngFor="let component of  this.dataModel.model.items;let i=index">
           
        
       <app-component-selector >
<--- Here -->
       <span dragHandle cdkDragHandle class="material-icons">
        open_with
        </span>
<--- Here -->
<div >
other data for view
</div>
</app-component-selector>

then in the sub child component I use this dragHandle where I need it

   <div class="col-2">
        <ng-content select="[dragHandle]">

        </ng-content>
       </div>
<ng-content></ng-content><-- Other data-->