Элементы абсолютной позиции Angular Drag and Drop выбирают неправильный индекс

Я пытаюсь перетащить элементы через два списка. Нижний список представляет собой типичный отсортированный список (например, «инвентарь»), но я хочу, чтобы верхние элементы были несортированными и выпадающими где угодно (например, «игровое поле»).

У меня это ПОЧТИ работает, однако при опускании в верхний ящик event.currentIndex всегда равен 0. Но при перетаскивании оттуда я получаю разные значения event.previousIndex, что означает, что элементы модели и DOM не всегда совпадают.

Вот stackblitz, показывающий, что я имею в виду. Перетащите несколько элементов в верхний ящик и поиграйте с ним, вы заметите, что иногда перемещается не тот элемент.

Это особенно заметно, когда вы взаимодействуете в обратном порядке, например:

  1. Перетащите элементы «Один», «Два», «Три» в верхнее поле (в указанном порядке)
  2. Попробуйте вернуть предметы «Три», «Два», «Один» обратно в нижнюю ячейку (в таком порядке)

введите здесь описание изображения


person Oren    schedule 03.05.2020    source источник


Ответы (1)


Опция cdkDropListSortingDisabled работает только при перемещении предметов в пределах одного контейнера. Если вы переходите из одного контейнера в другой, то Angular сортирует расположение блоков:

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

Поскольку вы не указали ориентацию, а по умолчанию используется вертикальная, она сортируется по позиции top.

Верхнее поле event.currentIndex всегда равно 0, потому что вы используете абсолютное позиционирование, а заполнитель всегда вверху.

Попробуйте добавить следующий стиль, чтобы увидеть, где отображается заполнитель:

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

введите здесь описание изображения

Чтобы исправить это, вы можете рассчитать currentIndex самостоятельно, например. как это:

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

Разветвленный Stackblitz

person yurzui    schedule 03.05.2020
comment
Проблема сохраняется, даже если не перетаскивать два списка. В разветвленной версии добавьте все три элемента, а затем просто перемещайте их внутри этой группы. Вы заметите, что время от времени кусок будет прыгать из-за неправильного индекса. (очень признателен за помощь кстати) - person Oren; 04.05.2020
comment
Я забыл проверить ту же группу. Не могли бы вы проверить это снова? stackblitz.com/edit/two-drop-list-problem-zp556d?file=src/app/ - person yurzui; 04.05.2020
comment
OMG вы решили это! Уже несколько дней ломаю голову над этим, тысм!! - person Oren; 04.05.2020
comment
Как это будет работать с двумя непересекающимися элементами на одной высоте (т.е. сверху)? Кажется, должен быть вторичный сорт... - person Oren; 05.05.2020
comment
Если вы хотите, вы также можете применить эту вторичную сортировку github.com/angular/components/blob/ - person yurzui; 05.05.2020