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);
}