3

I have a component based on this stackblitz: https://stackblitz.com/edit/angular-5mf7hl

The problem arises when I try to swap elements within the list. I want the array items to swap places. In the stackblitz example above, the targeted listItem gets pushed to frontwards or backwards during the preview in the list when it's being replaced by the sourceItem. I managed to make them swap places, by borrowing some code from Shinjo in this StackOverflow question: Angular 7 drag Drop swapping elements

The swapping itself works perfect, however, when I drag a listitem on top of another listitem, the preview isn't correct. When you release, it corrects itself and the listItem appears on the correct place within the array. What I want, is that the source list item and the target list item swap places during the preview, before letting go of the dragged item. You can have a look at the above stackblitz to visually see the problem.

App.component.ts

import { Component, NgModule, ViewChild } from '@angular/core';
import {
  CdkDrag,
  CdkDragStart,
  CdkDropList, CdkDropListContainer, CdkDropListGroup,
  moveItemInArray
} from "@angular/cdk/drag-drop";

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})

export class AppComponent {
@ViewChild(CdkDropListGroup) listGroup: CdkDropListGroup<CdkDropList>;
@ViewChild(CdkDropList) placeholder: CdkDropList;

public items: Array<number> = [1, 2, 3, 4, 5, 6, 7, 8, 9];

public target: CdkDropList;
public targetIndex: number;
public source: CdkDropListContainer;
public sourceIndex: number;

constructor() {
  this.target = null;
  this.source = null;
}

ngAfterViewInit() {
  let phElement = this.placeholder.element.nativeElement;

  phElement.style.display = 'none';
  phElement.parentNode.removeChild(phElement);
}

drop() {
  if (!this.target)
    return;

  let phElement = this.placeholder.element.nativeElement;
  let parent = phElement.parentNode;

  phElement.style.display = 'none';

  parent.removeChild(phElement);
  parent.appendChild(phElement);
  parent.insertBefore(this.source.element.nativeElement, 
  parent.children[this.sourceIndex]);

  this.target = null;
  this.source = null;

  if (this.sourceIndex != this.targetIndex) {
    let oldtarget = this.items[this.sourceIndex];
    this.items[this.sourceIndex] = this.items[this.targetIndex];
    this.items[this.targetIndex] = oldtarget;
  }
}

enter = (drag: CdkDrag, drop: CdkDropList) => {
  if (drop == this.placeholder)
  return true;

  let phElement = this.placeholder.element.nativeElement;
  let dropElement = drop.element.nativeElement;

  let dragIndex = __indexOf(dropElement.parentNode.children, 
  drag.dropContainer.element.nativeElement);
  let dropIndex = __indexOf(dropElement.parentNode.children, dropElement);

  if (!this.source) {
    this.sourceIndex = dragIndex;
    this.source = drag.dropContainer;

    let sourceElement = this.source.element.nativeElement;
    phElement.style.width = sourceElement.clientWidth + 'px';
    phElement.style.height = sourceElement.clientHeight + 'px';

    sourceElement.parentNode.removeChild(sourceElement);
  }

  this.targetIndex = dropIndex;
  this.target = drop;

  phElement.style.display = '';
  dropElement.parentNode.insertBefore(phElement, (dragIndex < dropIndex)
  ? dropElement.nextSibling : dropElement);

  this.source.start();
  this.placeholder.enter(drag, drag.element.nativeElement.offsetLeft, 
  drag.element.nativeElement.offsetTop);

  return false;
  }
}
function __indexOf(collection, node) {
  return Array.prototype.indexOf.call(collection, node);
};

App.component.html

<div class="example-container" cdkDropListGroup>
  <div cdkDropList [cdkDropListEnterPredicate]="enter" (cdkDropListDropped)="drop()" #placeholder></div>
  <div cdkDropList *ngFor="let item of items"
       [cdkDropListEnterPredicate]="enter" (cdkDropListDropped)="drop()">
    <div cdkDrag class="example-box">{{item}}</div>
  </div>
</div> 

App.component.css

.example-list {
  list-style-type: none;
  padding: 0;
}

.example-list li {
  display: table-cell;
  padding: 4px;
}

.example-container {
  display: flex;
  flex-wrap: wrap;
  min-width: 600px;
  max-width: 900px;
}

.example-box {
  width: 200px;
  height: 200px;
  border: solid 1px #ccc;
  font-size: 30pt;
  font-weight: bold;
  color: rgba(0, 0, 0, 0.87);
  cursor: move;
  display: flex;
  justify-content: center;
  align-items: center;
  text-align: center;
  background: #fff;
  border-radius: 4px;
  position: relative;
  z-index: 1;
  transition: box-shadow 200ms cubic-bezier(0, 0, 0.2, 1);
  box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2),
  0 2px 2px 0 rgba(0, 0, 0, 0.14),
  0 1px 5px 0 rgba(0, 0, 0, 0.12);
}

.example-box:active {
  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);
  opacity: .6;
}

.cdk-drop-list {
  display: flex;
  padding-right: 10px;
  padding-bottom: 10px;
}

.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: .3;
}

.cdk-drag-animating {
  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
Harry
  • 31
  • 1
  • 3
  • No worries. This question may be put on hold; if it does, ping me at `@halfer` and I will see if it can be reopened. – halfer Apr 14 '20 at 23:01
  • 1
    Thank you, halfer. I shall keep an eye on it. Your guidance is greatly appreciated. – Harry Apr 14 '20 at 23:05
  • @Harry I am trying to achieve the exact same thing and I am at the same stage as you were. I am surprised that the AM D&D does has not implemented this behavior natively as it seem less complicated then the behavior they currently support. Did you ever find a way around the issue? – norsemanGrey Oct 29 '21 at 07:52
  • Also looking for this solution to this! – Billy Apr 17 '22 at 18:53
  • Me too big time. It seems a fairly standard thing to do. 1) reorder 2) swap. Real shame there's no solution to be found. I may have to roll up my sleeves and fork angular material and see if I can do it myself. Sure, there's a hack to change the fina behaviour to perform as swap, but the UI still behaves WRONG while you are mi drag. what a drag – riketscience Aug 24 '23 at 13:14

0 Answers0