0
votes

I have a certain situation where I need to create a multi-list drag and drop in my project using the DragDrop service provided by Angular CDK. I am able to successfully move items around within its parent list, but when I try and transfer that item to a new list, I've discovered that the _draggables property does not update to transfer the item to the new list. Therefore, once I move the item to a new list, I can no longer drag it.

StackBlitz demo of the problem: https://stackblitz.com/edit/angular-kj7y4p

Alt Text

In this gif, I drag Get To Work to the second list, but I cannot pick it up after I drop it in the new list. At the very end I show that I can pick up the list item Get Up above it though (since that one was already in the array).

I create each list group like so:

const dropListRef1 = this.dragDrop.createDropList<string[]>(this.dropList1);

const dragListItems1: DragRef<any>[] = [];

this.dragItems1.forEach((item) => {
  let dragRef = this.dragDrop.createDrag(item);
  dragRef.data = item.nativeElement.innerHTML
  dragListItems1.push(dragRef);
})

dropListRef1.data = this.todo

dropListRef1.withItems(dragListItems1);

I listen for the dropped event, by subscribing to dropped:

dropListRef1.dropped.subscribe((event) => {
  this.drop(event);
})

This event is then used to either transfer the item to a new array or move the item within the array:

drop(event: any) {
  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);
  }
}

What configuration am I missing to make this work properly? Thanks!

EDIT:


Here's why I'm using cdkDrag service instead of just the directives.

Given the following data from my api:

var sections = [
  {
    "uid": "a",
    "order": 0,
    "layout": {
      "rows": [
        {
          "uid": "qz",
          "columns": [
            {
              "uid": "ugjm",
              "size": 6,
              "blocks": [ // NOTE: BLOCK WITH A COLUMN AS PARENT
                {
                  "uid": "bh",
                  "data": {
                    "content": "row qz, column ugjm, block bh"
                  },
                  "type": 2,
                  "order": 0
                }
              ]
            },
            {
              "uid": "plm",
              "size": 6,
              "rows": [ // NOTE: ROW WITH A COLUMN AS PARENT
                {
                  "uid": "bzx",
                  "columns": [
                    {
                      "uid": "1cd",
                      "size":  6,
                      "blocks": [
                        {
                          "uid": "nbv",
                          "data": {
                            "content": "row qz, row bzx, column 1cd, block nbv"
                          }
                        }
                      ]
                    }
                  ]
                }
              ]
            }
          ]
        },
      ],
      "columns": 12
    },
  }
]

I need to render these rows and columns on the page, to do this, I use ngTemplateOutlets to render the correct html elements on the page:

  <app-kit-section *ngFor="let section of sections" class="kit-section" [data]="section">
    <ng-container *ngIf="section.layout">
        <ng-template [ngTemplateOutlet]="recursiveTmpl" [ngTemplateOutletContext]="{ $implicit: section.layout }"></ng-template>
    </ng-container>
  </app-kit-section>

Then I look for which template to select here:

<ng-template #recursiveTmpl let-element>

  <ng-container *ngIf="element.rows && element.rows.length">
    <ion-row *ngFor="let row of element.rows">
      <ng-template [ngTemplateOutlet]="recursiveTmpl" [ngTemplateOutletContext]="{ $implicit: row }"></ng-template>
    </ion-row>
  </ng-container>

  <ng-container *ngIf="element.columns && element.columns.length">
    <ion-col *ngFor="let column of element.columns">
      <ng-template [ngTemplateOutlet]="recursiveTmpl" [ngTemplateOutletContext]="{ $implicit: column }"></ng-template>
    </ion-col>
  </ng-container>

  <ng-container *ngIf="element.blocks && element.blocks.length">
      <div *ngFor="let block of element.blocks">
        <ng-template [ngTemplateOutlet]="textTpl" [ngTemplateOutletContext]="{ $implicit: block }"></ng-template>
      </div>
  </ng-container>

</ng-template>

This renders an assortment of nested rows and columns on the page:

alt-text

This...was why i simplified my question and why i need to use the cdkService.

1
Try to use ajax it's much cleaner and easier. - ajmoprobatopet
How is Ajax even relevant to this question? - Jordan Lewallen
Why this custom logic when angular-material is providing this facility? - Gourav Garg
@GouravGarg because in my actual app, the list items and the drop list containers are dynamically added based on some other information so I don’t know how many drop lists or list items I’ll have until run time. They’re also rendered through an ngTemplateOutlet so I can’t use the cdkDrag and cdkDropList directives - Jordan Lewallen
Hi @JordanLewallen, trying to implement same as you did, but because of CDK drag drop orientation issue I am stuck. can you please provide your solution. - Ganesh Jangam

1 Answers

0
votes

As per your problem you have dynamic list/data and you want to change between these lists.

So what you can do create dynamic connectedTo between these lists.

<div class="example-container" *ngFor="let bucket of buckets">
  <h2>{{bucket.desc}}</h2>

  <div cdkDropList id="{{bucket.id}}" [cdkDropListData]="bucket.items"
      [cdkDropListConnectedTo]="connectedTo" (cdkDropListDropped)="drop($event)">
      <div class="example-box" cdkDrag *ngFor="let item of bucket.items" #dragItem1>{{item}}</div>
  </div>

In your ts file

for (let bucket of this.buckets) {
  this.connectedTo.push(bucket.id);
};
this.buckets=[
            {id:'todo',desc:'todo',items:['Get to work','Pick up groceries','Go home','Fall asleep']},
            {id:'done',desc: 'done',items:[Get up','Brush teeth','Take a shower','Check e-mail','Walk dog']}];

Here is the working stackblitz link.