Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular Drag and Drop absolute position elements selecting wrong index

I'm trying to drag items across two lists. The bottom list is a typical sorted list (like an "inventory"), but I want the top items to be unsorted and droppable anywhere (like a "game board").

I have it MOSTLY working, however when dropping into the top box event.currentIndex is always 0. But when dragging out from there I get different event.previousIndex values, which means the model and DOM elements don't always match.

Here's a stackblitz showing what I mean. Drag a few items into the top box and play around with it, you'll notice sometimes the wrong item gets moved.

It's most notable when you interact in reverse order, for example:

  1. Drag items "One", "Two", "Three" into the top box (in that order)
  2. Try to put back items "Three", "Two", "One" back into bottom box (in that order)

enter image description here

like image 819
Oren Avatar asked Jan 31 '26 17:01

Oren


1 Answers

cdkDropListSortingDisabled option only works when moving items within the same container. If you move from one container to another then Angular sorts position of blocks:

this._itemPositions = this._activeDraggables.map(drag => {
  const elementToMeasure = drag.getVisibleElement();
  return {drag, offset: 0, clientRect: getMutableClientRect(elementToMeasure)};
}).sort((a, b) => {
  return isHorizontal ? a.clientRect.left - b.clientRect.left :
                        a.clientRect.top - b.clientRect.top;
});

Since you didn't provide orientation and default is vertical then it is sorted by top position.

The top box event.currentIndex is always 0 because you use absolute positioning and placeholder is always at the top.

Try adding the following style to see where the placeholder is displayed:

.cdk-drag-placeholder {
  opacity: 1;
  background: red;
}

enter image description here

To fix it you can calculate currentIndex by yourself, e.g. like this:

const isWithinSameContainer = event.previousContainer === event.container;

let toIndex = event.currentIndex;
if (event.container.sortingDisabled) {
  const arr = event.container.data.sort((a, b) => a.top - b.top);
  const targetIndex = arr.findIndex(item => item.top > top);

  toIndex =
    targetIndex === -1
      ? isWithinSameContainer
        ? arr.length - 1
        : arr.length
      : targetIndex;
}

const item = event.previousContainer.data[event.previousIndex];
item.top = top;
item.left = left;

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

Forked Stackblitz

like image 70
yurzui Avatar answered Feb 02 '26 07:02

yurzui



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!