0
votes

I'm creating a simple drag&drop application in angular using angular CDK. I just want to drag my box and drop it to another box. But when releasing the dragged box inside the dropped area, I'm getting the error. Kindly help fixing it.

Error I'm getting

Unhandled Promise rejection: Failed to execute 'appendChild' on 'Node': The new child element contains the parent. ; Zone: <root> ; Task: Promise.then ; Value: DOMException: Failed to execute 'appendChild' on 'Node': The new child element contains the parent.
    at DragRef._cleanupDragArtifacts (http://localhost:4200/vendor.js:1615:121)
    at http://localhost:4200/vendor.js:1471:22
    at ZoneDelegate.invoke (http://localhost:4200/polyfills.js:3365:26)
    at Zone.run (http://localhost:4200/polyfills.js:3130:43)
    at http://localhost:4200/polyfills.js:3861:36
    at ZoneDelegate.invokeTask (http://localhost:4200/polyfills.js:3397:31)
    at Zone.runTask (http://localhost:4200/polyfills.js:3174:47)
    at drainMicroTaskQueue (http://localhost:4200/polyfills.js:3565:35)
    at ZoneTask.invokeTask [as invoke] (http://localhost:4200/polyfills.js:3475:21)
    at invokeTask (http://localhost:4200/polyfills.js:4609:14) Error: Failed to execute 'appendChild' on 'Node': The new child element contains the parent.
    at DragRef._cleanupDragArtifacts (http://localhost:4200/vendor.js:1615:121)
    at http://localhost:4200/vendor.js:1471:22
    at ZoneDelegate.invoke (http://localhost:4200/polyfills.js:3365:26)
    at Zone.run (http://localhost:4200/polyfills.js:3130:43)
    at http://localhost:4200/polyfills.js:3861:36
    at ZoneDelegate.invokeTask (http://localhost:4200/polyfills.js:3397:31)
    at Zone.runTask (http://localhost:4200/polyfills.js:3174:47)
    at drainMicroTaskQueue (http://localhost:4200/polyfills.js:3565:35)
    at ZoneTask.invokeTask [as invoke] (http://localhost:4200/polyfills.js:3475:21)
    at invokeTask (http://localhost:4200/polyfills.js:4609:14)
api.onUnhandledError @ zone-evergreen.js:651
handleUnhandledRejection @ zone-evergreen.js:675
api.microtaskDrainDone @ zone-evergreen.js:668
drainMicroTaskQueue @ zone-evergreen.js:566
invokeTask @ zone-evergreen.js:469
invokeTask @ zone-evergreen.js:1603
globalZoneAwareCallback @ zone-evergreen.js:1629

And I will paste my code below

My app.component.html

<section>
  <div class="wrapper">
    <div class="outerBox">
        <div class="innerBox" cdkDropList [cdkDropListConnectedTo]="[dropZone]" cdkDrag></div>
    </div>

    <div class="outerBox" #dropZone="cdkDropList" cdkDropList (cdkDropListDropped)="onItemDrop($event)">
    </div>
  </div>
</section>

and app.component.ts

import { Component } from '@angular/core';
import {CdkDragDrop, moveItemInArray, transferArrayItem} from '@angular/cdk/drag-drop';
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {

  onItemDrop(event) {
    console.log(event);
  }
}

and app.component.css file look like

.wrapper {
    display: flex;
}
.outerBox {
    width: 300px;
    height: 500px;
    border: 1px solid #333;
    padding: 10px;
    margin: 10px;
}
.innerBox {
    width: 100px;
    height: 100px;
    background: skyblue;
}

Kindly help in solving the error.

Thanks in advance.

2
You can refer - stackoverflow.com/questions/25216460/… . Hope it helps - Krunal Shah
Yeah, I too checked that. Its solution for javascript but I need a fix for angular drag & drop cdk. - Manush
I have created stackblitz for issue reproduction. You can check the console after dropping the bluebox. stackblitz.com/edit/angular-ebfpvj - Manush

2 Answers

0
votes

You need to define separate lists in your .ts file for storing the data e.g.

  list1 = ['hello1'];
  list2 = [];

In the .ts file in your onItemDrop function you also need to provide the code for transferring the data:

if (event.previousContainer === event.container) {
    moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
} else {
    transferArrayItem(
        event.previousContainer.data,
        event.container.data,
        event.previousIndex,
        event.currentIndex
    );
}

Then this code will work in your .html file:

<section>
    <div class="wrapper">
      <div class="outerBox" [cdkDropListData]="list1" id="dropZone1" cdkDropList [cdkDropListConnectedTo]="'dropZone2'" (cdkDropListDropped)="onItemDrop($event)">
          <div *ngFor="let item of list1" class="innerBox" cdkDrag></div>
      </div>
      <div class="outerBox" [cdkDropListData]="list2" id="dropZone2" [cdkDropListConnectedTo]="'dropZone1'" cdkDropList (cdkDropListDropped)="onItemDrop($event)">
        <div *ngFor="let item of list2" class="innerBox" cdkDrag></div>
      </div>
    </div>
</section>

In the above HTML, I fixed the following:

  • Separate the cdkDropList from the cdkDrag element.
  • Specify the connected ids correctly.
  • Use an *ngFor to iterate over the data to be displayed.
0
votes

Here steps you can follow to get started.

In Component class

  1. Define the 2 lists to connected each other.
  2. And implement moveItemInArray and transferArrayItem cdk helper methods: the first method is to handle change index in each list. And the second to handle transfer from list to an other list.

In template:

  1. Bind each list to a reference or id (e.g #listRef) container
  2. add cdkDropList, cdkDropListData, cdkDropListConnectedTo directives for each one
  3. add method to listen when cdkDropListDropped for both lists, here "onItemDrop"
  4. Iterate on each list with cdkDrag directive on each draggble item

I prepared a working stackblitz example.