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 находится внутри a * ngFor.

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

проблемная демонстрация

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

Спасибо


person sebamed    schedule 10.11.2018    source источник
comment
Будет ли ваш второй список работать, если вы удалите стиль, который делает его многострочным? Дроплисты могут быть только горизонтальными или вертикальными. Кажется, что у вас есть сетка, которая не будет работать, потому что существует внутренняя логика, которая полагается на знание того, следует ли рассчитывать относительные расстояния элементов списка в координатах x или y.   -  person Lightheaded    schedule 11.11.2018
comment
@Lightheaded - Да, я действительно подумал об этом и удалил все стили, но нет. Я нашел решение. Возникла проблема с cdkDropListConnectedTo. Он был связан с нулевым значением, поэтому я нашел обходной путь. Проверьте мой ответ ниже, и спасибо!   -  person sebamed    schedule 11.11.2018
comment
у вас есть рабочий пример этого?   -  person Muhammad Daniyal    schedule 15.07.2020


Ответы (6)


После целого дня исследований я нашел этот запрос на перенос в репозитории 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- это префикс идентификатора. CDK помещает этот префикс в каждый элемент с атрибутом cdkDropList)

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

  • cdk-drop-list-00
  • cdk-drop-list-01
  • cdk-drop-list-02
  • и т.п.

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

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

И работает безотказно!

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

person sebamed    schedule 11.11.2018
comment
cdkDropListGroup - это еще не выпущенная функция. Следите за следующими выпусками. Между тем, я использую тот же подход - использую сопоставление по идентификаторам списков. На данный момент нет лучшего способа сделать это. Всякий раз, когда ваша упомянутая функция будет выпущена, вы можете удалить всю эту хакерскую возню с идентификаторами :) - person Lightheaded; 12.11.2018
comment
Выпущен cdkDropListGroup. Могу ли я получить эту демонстрацию с помощью cdkDropListGroup где угодно? - person Jomy Joseph; 23.11.2018
comment
Я добавил демонстрацию cdkDropListGroup в StackBlitz по адресу stackblitz.com/edit/angular-a4ftm7 - person Maxxx; 27.11.2018
comment
Я тоже пытаюсь добиться того же. Кто-нибудь может мне с этим помочь. Найдите ссылку. stackoverflow.com/questions/59386696/ - person Rahul Rai; 19.12.2019
comment
Этот подход отлично подходит для строк, которые могут уместиться в диалоговом окне. Однако перетаскивание не может определить текущий индекс, когда слишком много строк не помещается в видимой области. Если я выберу последнюю строку и попытаюсь переместить ее вверх с помощью вертикальной полосы прокрутки, это не удастся. Есть идеи решить проблему? - person user3097695; 19.11.2020

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

Демо Ссылка

Для списков динамического перетаскивания мы можем использовать ID вместо переменных # Template.

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

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);
    }
  }
}
person Code Spy    schedule 12.04.2019

Теперь с помощью 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>

В этом случае больше не требуется cdkDropListConnectedTo. См. https://github.com/angular/material2/blob/master/src/cdk/drag-drop/drag-drop.md

person avocadocommander    schedule 25.11.2018

Мне тоже пришлось столкнуться с этой проблемой. Я пробовал подход id, но не чувствовал себя слишком уверенно при использовании. Когда я использую 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 имеет свойства изменения, которые являются Observable.

person Benoît Plâtre    schedule 07.10.2019

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

person James D    schedule 07.01.2020

Немного поздно, но полезно и стоит поделиться. Я создал доску канбан с использованием angular материала версии 10. Вот ссылка на демонстрацию. https://stackblitz.com/edit/angular-material-kanban

person The Mechanic    schedule 09.08.2020