29

I am trying to implement an editor where one can drag an item to add it to the main content, the problem is that the source item is always destroyed when I'm dragging out of the source item container.

Is there a way to force the source item to stay where it is while it's still possible to drag and drop the item? Basically, I want a copy-behaviour of that instead of a move-behaviour.

I've already seen other questions that correspond to what I want to achieve basically, but none of them really helped me as the questions were more about how to get it technically done to copy an item while I want to know how I implement this behaviour in the UI as its very confusing to see if the item just disappears.

B.Mayssa
  • 94
  • 13
Yassine Zeriouh
  • 480
  • 3
  • 10
  • 23
  • Possible duplicate of [Drag Drop CDK: keep showing dragged element inside starting list](https://stackoverflow.com/questions/55759989/drag-drop-cdk-keep-showing-dragged-element-inside-starting-list) – Federico Galfione May 13 '19 at 14:40

4 Answers4

27

replace

drop(event: CdkDragDrop<string[]>) {
  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
    );
  }
}

with

drop(event: any) {
  if (event.previousContainer === event.container) {
    moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
  } else {
    copyArrayItem(
      event.previousContainer.data,
      event.container.data,
      event.previousIndex,
      event.currentIndex
    );
  }
}
Kishan Bharda
  • 5,446
  • 3
  • 30
  • 57
arpit tayal
  • 296
  • 4
  • 3
  • 6
    Thanks for this solution. However, with this behaviour, there is a style issue on the list from which are moving the element. It fluctuates the list as it looks like it removes and then adds the element on the list for the copy operation. Please refer to this stackblitz link - https://stackblitz.com/edit/angular-g5uzys?file=app%2Fcdk-drag-drop-connected-sorting-example.ts – Himanshu Nov 12 '21 at 09:29
  • Yes, do we even need `copyArrayItem`? I've got this working with the same UX by simply NOT updating the source list in the `drop` method. Agreed, the visual style of dragging OUT is the issue. – Ben Racicot Jul 05 '22 at 16:53
6

Import import {copyArrayItem} from '@angular/cdk/drag-drop';

AND

Replace transferArrayItem with copyArrayItem

3

All solutions above are great however there is one problem cloning happens after element is dropped so I went through git conversion and found the solution below

STYLES

.drag-container {
    width: 400px;
    max-width: 100%;
    margin: 0 25px 25px 0;
    display: inline-block;
    vertical-align: top;
}

.drag-list {
    border: solid 1px #ccc;
    min-height: 60px;
    background: white;
    border-radius: 4px;
    overflow: hidden;
    display: block;
}

.drag-box {
    padding: 20px 10px;
    border-bottom: solid 1px #ccc;
    color: rgba(0, 0, 0, 0.87);
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    box-sizing: border-box;
    cursor: move;
    background: white;
    font-size: 14px;
}

.cdk-drag-preview {
    box-sizing: border-box;
    border-radius: 4px;
    box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),
        0 8px 10px 1px rgba(0, 0, 0, 0.14),
        0 3px 14px 2px rgba(0, 0, 0, 0.12);
}

.cdk-drag-placeholder {
    opacity: 0;
}

.cdk-drag-animating {
    transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}

.drag-box:last-child {
    border: none;
}

.drag-list.cdk-drop-list-dragging .drag-box:not(.cdk-drag-placeholder) {
    transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}

TYPESCRIPT

menu: any = [
    { title: 'pork', price: 12, id: 1 },
    { title: 'duck', price: 12, id: 2 },
    { title: 'chicken', price: 12, id: 3 },
    { title: 'beef', price: 12, id: 4 },
    { title: 'soup', price: 12, id: 5 },
  ];

  table: any = [];



  drop(event: any) {
    if (event.previousContainer !== event.container) {
      copyArrayItem(
        event.previousContainer.data,
        event.container.data,
        event.previousIndex,
        event.currentIndex
      );
    } else {
      moveItemInArray(
        event.container.data,
        event.previousIndex,
        event.currentIndex
      );
    }
    if (event.previousContainer.data) {
      this.menu = this.menu.filter((f: any) => !f.temp);
    }
  }



  exited(event: any) {
    const currentIdx = event.container.data.findIndex(
      (f: any) => f.id === event.item.data.id
    );
    this.menu.splice(currentIdx + 1, 0, {
      ...event.item.data,
      temp: true,
    });
  }
  entered() {
    this.menu = this.menu.filter((f: any) => !f.temp);
  }

HTML

<div cdkDropListGroup>
            <div class="drag-container">
                <h2>To do</h2>
                <div class="drag-list" cdkDropList #menuList="cdkDropList" [cdkDropListData]="menu" cdkDropListSortingDisabled [cdkDropListConnectedTo]="[tableList]" (cdkDropListDropped)="drop($event)" (cdkDropListExited)="exited($event)" (cdkDropListEntered)="entered()">
                    <div class="drag-box" *ngFor="let item of menu" cdkDrag [cdkDragData]="item">{{item.title}}</div>
                </div>
            </div>
            <div class="drag-container">
                <h2>Done</h2>
                <div class="drag-list" cdkDropList #tableList="cdkDropList" [cdkDropListData]="table" (cdkDropListDropped)="drop($event)">
                    <div class="drag-box" *ngFor="let item of table; let idx = index" cdkDrag>{{item.title}}</div>
                </div>
            </div>
</div>

Source 1 : stackblitz

Source 2 : github

Vikas Kandari
  • 1,612
  • 18
  • 23
  • 1
    +1 This solution better answers the original question. Using Exited and Entered events makes for a smoother ux. – Emmanuel Sep 01 '22 at 12:17
0

You need to find location of target & source then copy the value

Case 1: Copy from one draggable list to another draggable list

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

              const prev_idx = event.previousIndex;
              const curr_id = event.currentIndex;
              // Copy the data.
              event.container.data[curr_id] = event.previousContainer.data[prev_idx];
        
            }
          }
    }

Case 2: Copy your Data from list to a specific variable

drop(event: any) {
          // Copy the data to my-variable
                  const prev_idx = event.previousIndex;    
                  this.my-variable = event.previousContainer.data[prev_idx];
            
                }
              }
        }
Najim Ali
  • 191
  • 1
  • 4