Angular 7 Drag and Drop - динамически создавайте зоны падения

Есть ли способ динамически создавать зоны сброса? У меня проблемы с ngFor и cdkDropList.

Вот мой первый список и перетаскиваемые элементы:

       <div class="subj-container" 
        cdkDropListOrientation="horizontal" 
        cdkDropList 
        #subjectList="cdkDropList"
        [cdkDropListData]="subjects"  
        [cdkDropListConnectedTo]="[lessonList]" 
        (cdkDropListDropped)="drop($event)"
        >
            <div class="subject" *ngFor="let subject of subjects" cdkDrag>
                {{subject.name}}
            </div>
        </div>

А вот и мой второй список:

          <div class="conta" cdkDropList
                #lessonList="cdkDropList"
                [cdkDropListData]="appointment.lessons"
                [cdkDropListConnectedTo]="[subjectList]"
                (cdkDropListDropped)="drop($event)">
                    <div class="sub" cdkDrag *ngFor="let lesson of appointment.lessons">
                        {{lesson.name}}
                </div>
           </div>

Теперь div с классом 'conta' находится внутри * ngFor.

Моя проблема, я полагаю, с моим вторым списком. Если я перетаскиваю элемент из второго списка в список один, он работает нормально, но если я пытаюсь перетащить элемент из списка один в любой экземпляр списка во втором списке, он не может распознать, что элемент перетаскивается. Демо здесь:

problem demo

Я что-то здесь не так делаю? Машинописная часть работает нормально.

Спасибо

Ответ 1

После целого дня исследований я нашел этот запрос на загрузку репозитория Angular CDK на Github. Теперь, так как я не знал, как интегрировать cdkDropListGroup в мой пример, я решил создать массив идентификаторов, которые будут добавлены в [cdkDropListConnectedTo].

Каждый экземпляр моего второго списка будет генерировать идентификатор, и этот идентификатор будет добавлен в массив с подходящим префиксом (в моем втором списке, в cdkDropList):

<div cdkDropList
      [attr.id]="addId(i, j)"
      [cdkDropListData]="appointment.lessons"
      [cdkDropListConnectedTo]="[subjectList]"
      (cdkDropListDropped)="drop($event)"
>

метод addId:

addId(i, j) {
    this.LIST_IDS.push('cdk-drop-list-' + i + '' + j);
    return i + '' + j;
}

(cdk-drop-list- это префикс ID. CDK помещает этот префикс в каждый элемент с атрибутом cdkDropList)

Итак, мой массив будет выглядеть так:

  • CDK-раскрывающегося список-00
  • CDK-раскрывающегося список-01
  • CDK-раскрывающегося список-02
  • и т.п.

Теперь я передаю этот массив [cdkDropListConnectedTo] в моем первом списке:

<div class="subj-container" 
    cdkDropListOrientation="horizontal"
    cdkDropList 
    #subjectList="cdkDropList"            
    [cdkDropListData]="subjects" 
    [cdkDropListConnectedTo]="LIST_IDS"
    (cdkDropListDropped)="drop($event)"
>

И это работает без нареканий!

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

Ответ 2

С помощью cdkDropListGroup вы можете сделать следующее:

<div cdkDropListGroup>

  <div cdkDropList
    [cdkDropListData]="data"
    (cdkDropListDropped)="drop($event)">
    <div class="row m-2">
        <div *ngFor="let i of data cdkDrag>{{i}}</div>          
    </div>
  </div>

  <div class="subj-container" 
    cdkDropListOrientation="horizontal"
    cdkDropList 
    #subjectList="cdkDropList"            
    [cdkDropListData]="subjects" 
    (cdkDropListDropped)="drop($event)"> 
  </div>

</div>

Ответ 3

Ссылка на источник

Демо- ссылка

Для Dynamic Drag n Drop Lists мы можем использовать ID вместо # Template переменных

enter image description here

app.component.html

<div class="col-md-3" *ngFor="let week of weeks">
  <div class="drag-container">
    <div class="section-heading">Week {{week.id}}</div>

    <div cdkDropList id="{{week.id}}" [cdkDropListData]="week.weeklist"
      [cdkDropListConnectedTo]="connectedTo" class="item-list" (cdkDropListDropped)="drop($event)">
      <div class="item-box" *ngFor="let weekItem of week.weeklist" cdkDrag>Week {{week.id}} {{weekItem}}</div>
    </div>
  </div>
</div>

app.component.ts

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

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

  weeks = [];
  connectedTo = [];


  constructor() {
    this.weeks = [
      {
        id: 'week-1',
        weeklist: [
          "item 1",
          "item 2",
          "item 3",
          "item 4",
          "item 5"
        ]
      }, {
        id: 'week-2',
        weeklist: [
          "item 1",
          "item 2",
          "item 3",
          "item 4",
          "item 5"
        ]
      }, {
        id: 'week-3',
        weeklist: [
          "item 1",
          "item 2",
          "item 3",
          "item 4",
          "item 5"
        ]
      }, {
        id: 'week-4',
        weeklist: [
          "item 1",
          "item 2",
          "item 3",
          "item 4",
          "item 5"
        ]
      },
    ];
    for (let week of this.weeks) {
      this.connectedTo.push(week.id);
    };
  }

  drop(event: CdkDragDrop<string[]>) {
    if (event.previousContainer === event.container) {
      moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
    } else {
      transferArrayItem(event.previousContainer.data,
        event.container.data,
        event.previousIndex,
        event.currentIndex);
    }
  }
}

Ответ 4

Мне также пришлось столкнуться с этой проблемой. Я попробовал идентификационный подход, но я не чувствовал себя слишком уверенно при использовании. Когда я console.log в этой функции addId(), я вижу один и тот же идентификатор, повторенный несколько раз. Вместо этого я попытался использовать декоратор @ViewChildren для работы с компонентами cdkList в реальном времени, и он очень хорошо работает для меня.

В машинописи

  cdkDropTrackLists: CdkDropList[];
  @ViewChildren(CdkDropList)
  set cdkDropLists(value: QueryList<CdkDropList>) {
    this.cdkDropTrackLists = value.toArray();
  }

В шаблоне

<div
        cdkDropList
        class="track-list"
        cdkDropListSortingDisabled
        [cdkDropListData]="paragraphIdentifiers"
        (cdkDropListDropped)="drop($event)"
        [cdkDropListConnectedTo]="cdkDropTrackLists">
</div>

Я думаю, что могу улучшить его, в то время как cdkDropLists как QueryList имеет свойства изменений, которые можно наблюдать.