Как использовать Angular CDK DragDrop с предопределенными слотами?

StackBlitz: https://stackblitz.com/edit/league-predictions

У меня есть проект, в котором я хочу прогнозировать турнирную таблицу футбольной лиги. У меня есть два списка: один - с прогнозами, второй - со всеми командами.

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

Я не могу найти в Интернете решение, как этого добиться.

Это моя текущая ситуация, поэтому вы можете понять, о чем я говорю: Прогнозы лиги

И это то, чего я хочу достичь.  введите описание изображения здесь

Кто-нибудь знает, как предопределить слоты для Angular CDK DragDrop

Это мой текущий код.

<div class="container">
  <div class="example-container">
    <h5>Predictions</h5>
    
    <div
      cdkDropList
      #predictionsList="cdkDropList"
      [cdkDropListData]="predictions"
      [cdkDropListConnectedTo]="[teamList]"
      class="example-list"
      (cdkDropListDropped)="drop($event)"
    >
  
      <div class="example-box" *ngFor="let prediction of predictions; let i = index" cdkDrag>
        <app-team [team]="prediction" [index]="i"></app-team>
      </div>
  
    </div>
  </div>
  
  <div class="example-container">
    <h5>Teams</h5>
  
    <div
      cdkDropList
      #teamList="cdkDropList"
      [cdkDropListData]="teams"
      [cdkDropListConnectedTo]="[predictionsList]"
      class="example-list"
      (cdkDropListDropped)="drop($event)"
    >
  
    <div class="example-box" *ngFor="let team of teams;" cdkDrag>
      <app-team [team]="team"></app-team>
    </div>
  
    </div>
  </div>
</div>

Не обращайте внимания на длинный список команды, это все данные из базы данных

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

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

  ngOnInit(): void {
  }

  predictions = [
  ];

  teams = [
    {
      name: 'ADO Den Haag',
      logo: 'Ado-Den-Haag-Logo.png'
    },
    {
      name: 'Ajax',
      logo: 'AFC-Ajax-Logo.png'
    },
    {
      name: 'AZ',
      logo: 'AZ-Alkmaar-Logo.png'
    },
    {
      name: 'FC Emmen',
      logo: 'FC-Emmen-Logo.png'
    },
    {
      name: 'FC Groningen',
      logo: 'FC-Groningen-Logo.png'
    },
    {
      name: 'FC Twente',
      logo: 'fc-twente-logo.png'
    },
    {
      name: 'FC Utrecht',
      logo: 'FC-Utrecht-Logo.png'
    },
    {
      name: 'Feyenoord',
      logo: 'Feyenoord-Rotterdam-Logo.png'
    },
    {
      name: 'Fortuna Sittard',
      logo: 'Fortuna-Sittard-Logo.png'
    },
    {
      name: 'Heracles',
      logo: 'Heracles-Almelo-Logo.png'
    },
    {
      name: 'PEC Zwolle',
      logo: 'PEC-Zwolle-Logo.png'
    },
    {
      name: 'PSV',
      logo: 'PSV-Eindhoven-Logo.png'
    },
    {
      name: 'RKC Waalwijk',
      logo: 'rkc-waalwijk.png'
    },
    {
      name: 'SC Heerenveen',
      logo: 'SC-Heerenveen-Logo.png'
    },
    {
      name: 'Sparta Rotterdam',
      logo: 'Sparta_Rotterdam_logo.png'
    },
    {
      name: 'Vitesse',
      logo: 'Vitesse-Arnhem-Logo.png'
    },
    {
      name: 'VVV Venlo',
      logo: 'VVV-Venlo-Logo.png'
    },
    {
      name: 'Willem II',
      logo: 'Willem-II-Logo.png'
    },
  ];

  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 Marcel    schedule 24.08.2020    source источник
comment
Намного проще, если вы создадите образец на stackblitz.com   -  person Zeeshan Siddiqui    schedule 27.08.2020
comment
Мне удалось заставить работать StackBlitz. stackblitz.com/edit/league-predictions   -  person Marcel    schedule 28.08.2020


Ответы (2)


сделал stackblitz, описывающий, что делать, сначала создайте фиктивные записи в массиве прогнозов:

  getPrediction(): Team[] {
    let localStorageItem = JSON.parse(localStorage.getItem(this.league.name));
    return localStorageItem == null ? this.getTeams().map(t => ({})) : localStorageItem.standings;
  }

это заполнит ваши слоты. отключите перетаскивание этих элементов:

<div class="example-box" *ngFor="let prediction of predictions; let i = index" cdkDrag [cdkDragDisabled]="!prediction.name">

Затем вам нужно добавить параметр в вашу drop функцию, чтобы знать, какой контейнер сбрасывается:

drop(event: CdkDragDrop<Team[]>, droppedOn: 'teams' | 'predictions') {

и шаблон обновляется соответствующим образом:

(cdkDropListDropped)="drop($event, 'predictions')"

 ...

(cdkDropListDropped)="drop($event, 'teams')"

в своей функции перетаскивания вы используете этот параметр в случае передачи списка, чтобы добавить или удалить заполнитель:

  if (droppedOn === 'teams') {
    // moved back to teams, need to re add placeholder
    this.predictions.push({});
  } else {
    // otherwise, removing a placeholder
    // find the idx to remove, first placeholder at or below current idx
    let removeIdx = this.predictions.findIndex((t, i) => i >= event.currentIndex && !t.name);
    if (removeIdx < 0) {
      // or just the first available.
      removeIdx = this.predictions.findIndex(t => !t.name);
    }
    this.predictions.splice(removeIdx, 1);
  }
  transferArrayItem(event.previousContainer.data,
                    event.container.data,
                    event.previousIndex,
                    event.currentIndex);

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

рабочий блиц: https://stackblitz.com/edit/league-predictions-ouu8nr?file=src%2Fapp%2Fprediction%2Fprediction.component.ts

person bryan60    schedule 28.08.2020
comment
Как вы сказали, я могу кое-что исправить. Но это близко к тому, что мне нужно. Спасибо! - person Marcel; 28.08.2020

Что ж, без рабочего stackblitz трудно предоставить какие-либо полезные образцы кода, но я попробую.

Для левого списка я бы создал массив с пустыми объектами того же размера, что и список ваших команд.

Я бы создал {name: null, logo: null} записи и сделал бы отметку в шаблоне, чтобы ничего не отображалось if name === null

Расширьте обработчик событий drop, добавьте проверку if (dropTarget.name === null) и замените фиктивную запись своим значением. В противном случае сохраните существующую логику

Изменить: базовый пример Stackblitz: Stackblitz < / а>

person Nicolas Gehlert    schedule 28.08.2020
comment
обновил свой ответ примером stackblitz. шаги довольно просты, как я описал в своем посте :) - person Nicolas Gehlert; 28.08.2020
comment
здесь есть несколько угловых случаев: перемещение команд обратно в исходный массив и удаление команды из команды в массиве прогнозов, а также предотвращение перемещения элементов-заполнителей. - person bryan60; 28.08.2020
comment
ну, я не буду делать за тебя всю работу. Это рабочее доказательство концепции. - person Nicolas Gehlert; 29.08.2020