Angular Cdk Drag and drop, списки постоянной длины

Я уже довольно давно ломаю голову над этой проблемой. Я хочу добиться двух связанных перетаскиваемых списков постоянной длины. Это означает, что если я перемещаю элемент из одного списка в другой, элемент перемещается в другой список. Это, конечно, тривиально во время события cdkDropListDropped, но я хочу, чтобы это произошло, как только элемент будет перемещен по списку.

Большинство моих попыток были связаны с использованием события cdkDropListEntered для:

  1. Попытайтесь просто переместить элемент в массиве данных:
public enter(list: number, event: CdkDragEnter<User[]>) {
  if (list === 0) {
    let data = this.schedule.responsible.pop();
    this.schedule.queue.unshift(data);
  } else {
    let data = this.schedule.queue.shift();
    this.schedule.responsible.push(data);
  }
}

Это привело к ошибкам типа:

core.js: 6185 ОШИБКА DOMException: не удалось выполнить 'insertBefore' на 'Node': узел, перед которым должен быть вставлен новый узел, не является дочерним по отношению к этому узлу

  1. Пробовал использовать CdkDropList addItem(), removeItem(), getSortedItems(). Это привело к аналогичным проблемам.

  2. Пытался переместить сами DOM-элементы с помощью Renderer2 (и оставить данные нетронутыми)

Есть ли способ добиться того, чего я хочу?

Этот великолепный рисунок краской помогает объяснить, чего я хочу достичь.


person Kim Ulvik    schedule 10.06.2020    source источник


Ответы (1)


Хорошо, я понял это после попытки двух решений. Первый включал добавление полей-заполнителей в оба списка, которые были видны только в том случае, если в них был контент. Их содержимое будет содержимым поля, помещенного в этот список. Между тем оригинальной коробке был придан стиль display: none. Это в основном достигло того поведения, которое я хотел, но возникло несколько визуальных проблем из-за несоответствия между внутренней моделью перетаскиваемого объекта и DOM.

Что в конечном итоге сработало, так это отказ от концепции наличия двух списков. Затем сортировка разрешилась сама собой. Тем не менее, стиль должен быть выполнен немного иначе, поскольку каждый перетаскиваемый объект должен быть прямым потомком списка.

Прикрепляем код и рабочий пример Stackblitz:

app.component.ts

import { Component, OnInit} from '@angular/core';
import {CdkDragDrop, CdkDropList, moveItemInArray } from '@angular/cdk/drag-drop';


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

    public lists: {list1: string[], list2: string[]};
    public fullList: string[];
    public numList1: number;

    constructor() {}

    ngOnInit() {
        this.lists = {
            list1: ['one', 'two'],
            list2: ['three', 'four']
        };
        this.fullList = this.lists.list1.concat(this.lists.list2);
        this.numList1 = this.lists.list1.length;
    }

    public drop(event: CdkDragDrop<string[]>) {
        moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
    }
}

app.component.html

<div class="list-container">
  <div cdkDropList
      [cdkDropListAutoScrollDisabled]="true"
      [cdkDropListData]="fullList"
      cdkDropListLockAxis="y"
      (cdkDropListDropped)="drop($event)">
    <ng-container *ngFor="let item of fullList; let index = index;">
      <h2 *ngIf="index === 0">List 1</h2>
      <h2 *ngIf="index === numList1">List 2</h2>
      <div cdkDrag class="drop-box">{{item}}</div>
    </ng-container>
  </div>
</div>

Stackblitz: https://stackblitz.com/edit/angular-ivy-s7zfye

person Kim Ulvik    schedule 11.06.2020