Components: Drag and Drop list ordering issue

Created on 17 Jan 2019  Â·  30Comments  Â·  Source: angular/components

What is the expected behavior?

I am trying to reproduce the simple drag and drop list ordering example from here: https://material.angular.io/cdk/drag-drop/overview

What is the current behavior?

However, the dragged item always snaps back to its original place. The ordering animation is showing. The event shows that the previous and the current index are always 0.
https://i.imgur.com/1RkV6MT.gifv
https://i.imgur.com/k4wMbt3.gifv

What are the steps to reproduce?

The drop listener and the test data:

strings = ["dsfs","fgjdf","ghama","jéép"];
drop(event: CdkDragDrop<String[]>) {
    console.log(event);
    moveItemInArray(this.strings, event.previousIndex, event.currentIndex);
}

The HTML:

<div cdkDropList *ngFor="let string of strings" [cdkDropListData]="strings" (cdkDropListDropped)="drop($event)">
    <mat-card cdkDrag [cdkDragData]="string">{{string}}</mat-card>
</div>

The CSS is copy-paste from the tutorial

Which versions of Angular, Material, OS, TypeScript, browsers are affected?

Angular: 6.2.1
Material: 7.2.1
OS: Ubuntu 16.04
TypeScript: 3.2.2
Chrome: 65.0.3325.146

P3 cddrag-drop

Most helpful comment

Any updates on this? This is a very annoying issue where the prev and current index is most of the time the same number regardless of which item i drag and where i drop it.

All 30 comments

have you tried wrapping the mat-card into a <div> that has all the directives?

I think you need this. Its work fine, for me. You can get the array, from the event object.
drop(event: any): void { 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); } }

To use a CDKDropDropList you must implement the cdkDropListDropped event and re-arrange the array you provide the values from yourself so that they re re-ordered on the DOM.

@Flashunt has provided a great example of the simplest way to do this, using the provided moveItemInArray method (which must be imported from the material library).

However if you other requirements you can use any method you wish to re-order the entities you provide as cdk elements. The CdkDragDrop event object has current and previous indexes for you to use.

I am having same or similar issues, were the indexes seem to not always be correct.

const dropListRef = this.dragDrop
  .createDropList(this.formlyForm.nativeElement)
  .withItems(Array.from(this.formlyForm.nativeElement.querySelectorAll('formly-field')).map((field: HTMLElement) => {
    return this.dragDrop.createDrag(field);
  }));
dropListRef.lockAxis = 'y';
dropListRef.dropped.subscribe(({ previousIndex, currentIndex }) => {
  // BUG: previousIndex, currentIndex are not always updated and often the same number so nothing happens...
  console.log(`previousIndex: ${previousIndex}, currentIndex: ${currentIndex}`);
  moveItemInArray(this.fields, previousIndex, currentIndex);
});

image

gif

Any updates on this? This is a very annoying issue where the prev and current index is most of the time the same number regardless of which item i drag and where i drop it.

I think the fastest way to get help is posting an stackblitz that repoduces your error.

so, i did reproduce this bug when i have custom style, but working fine if i copy paste styles from example.

Probably the problem in flexbox

Yeah I am using flexbox, did you reproduce at stackblitz?

Same here, list re-ordering works fine when elements have display:block, doesn't work when have display:flex

I have very similar problem. I'm usingBehaviorSubject as DataSource.
After first reorder it keeps old index.
Reproduction here:
https://stackblitz.com/edit/angular-3ajhey
Any ideas why and how to fix it?

I find out if I reset first our dataSource and then emit new value then it works fine. Still don't know why.

this.dataSource.next(null);
this.dataSource.next(currentValue);

https://stackblitz.com/edit/angular-ggzjeh

It seems to be a css issue, dont know exactly what css causes the behavior but a colleague played around with the css and the issues went away, just FYI...

So any workaround for this when using flex version of mat-table ?

I'm getting the same issue. For the moment I don't use the previousIndex property and I'm using :

const computedPreviousIndex = datasource.indexOf(event.item.data, 0);
I will continue to test my work around but it seams to work (in my first tests)

For now my workaround was just to use some unique identifier of items to switch in collection

Not really, it seems that CdkDrag is resolving an unhandled promise inside, just use setTimeout(() => yourCb, 0); to schedule it for the next event loop.

Any updates on this? I am facing this issue with the version 9.1.0.

I got it working with the below codes.

html

<div>
  <div cdkDropList
         class="cdkdrop-list"
         (cdkDropListDropped)="drop($event)">
    <div *ngFor="let item of items;"
        cdkDrag
        class="cdkdrag-box">
      <app-slide-selector-item [selector]="item">
      </app-slide-selector-item>
    </div>
  </div>
</div>

typescript

drop(event: any) {
    moveItemInArray(this.items, event.previousIndex, event.currentIndex);
    this.slideOrderChanged.next([...this.items]);
  }

CSS

.cdkdrop-list {
  display: block;
}

.cdkdrag-box {
  display: block;
}

The important thing is that both .cdkdrop-list and .cdkdrag-box display property is block, it will not work if you chose flex. The same is not working if you assign float property too.

@crisbeto This bug is killing me, have any ideas? None of the work-arounds mentioned here are working for me

Hi!
Could you put a link to check it / reproduce it?

Okay found a work-around that actually works for me:

The key was: this.datasource = [...this.datasource];

For the method called when dropped
(cdkDropListDropped)="moveDraggedDimension($event)"

```
moveDraggedDimension(event: CdkDragDrop moveItemInArray(this.datasource, event.previousIndex, event.currentIndex);
this.datasource = [...this.datasource];
}

Where this.datasoure is a simple array[]

@cyriljoui I will try to reproduce it on stackblitz as soon as my PM gives me a chance

Having the same problems, none of the solutions proposed here works for me.
@rsryanstephen did you had a chance creating a stackblitz?

@cyriljoui @crisbeto when you drag and drop an item at the 4th position in this stackblitz:

https://stackblitz.com/edit/cdk-drag-drop-index-bug-vaqfmc

What are the steps to reproduce it @rsryanstephen? From what I can tell it works as expected, at least for the first drag. The subsequent drags seem to break down, but from what I can tell, it's because one of the arrays isn't being updated.

Yea the positions of the elements are not being updated correctly. That's
essentially where the bug lies.

On Tue, 23 Jun 2020 at 20:15, Kristiyan Kostadinov notifications@github.com
wrote:

What are the steps to reproduce it @rsryanstephen
https://github.com/rsryanstephen? From what I can tell it works as
expected, at least for the first drag. The subsequent drags seem to break
down, but from what I can tell, it's because one of the arrays isn't being
updated.

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/angular/components/issues/14873#issuecomment-648332162,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AHLGL6YNKDNEUWGEN745JWLRYDWMPANCNFSM4GQ3ODOA
.

@cyriljoui @crisbeto when you drag and drop an item at the 4th position in this stackblitz: https://stackblitz.com/edit/cdk-drag-drop-index-bug-vaqfmc

In this stackblitz the array seems to be updated in drop(... => moveItemInArray(this.items, event.previousIndex, event.currentIndex);

(first position starts with 0)

  1. move Item1 to position 1
  2. Item2 is in position 0 Item1 is in position 1
  3. the array is updated everything works correct!
  4. just drag Item1 without sorting.
  5. Its not placed in position 1 again
  6. Its placed in position 0.
  7. looks like a bug to me, at least im not seeing what is wrong

The problem is that you're updating the data, but you also have to update the items that are passed to the DropListRef.withItems. The drop list doesn't know about your data so it can't update its internal references automatically. We handle this case in CdkDropList by calling withItems on every beforeStarted event. See https://github.com/angular/components/blob/master/src/cdk/drag-drop/directives/drop-list.ts#L285.

You will need to use preventDefault(); function

A good trackBy function will be a good solution, for most of the questions here.

Was this page helpful?
0 / 5 - 0 ratings