Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bug(drag-drop + virtual scroll): Draggable item loses its position when new list item is rendered in the DOM as a result of virtual scrolling #19003

Closed
maxkoretskyi opened this issue Apr 6, 2020 · 3 comments
Assignees

Comments

@maxkoretskyi
Copy link

maxkoretskyi commented Apr 6, 2020

Reproduction

The problem comes up with you mix together drag-and-drop and virtual scroll functionality. Here's the stackblitz that demonstrates the problem. I've painted the placeholder element into green.

I've done a lot of debugging to figure out the cause of the problem and I'll describe my findings below.

Steps to reproduce:

  1. Configure component view to use both drag-and-drop and virtual scrolling
<cdk-virtual-scroll-viewport class="virtual-scroll-container" itemSize="30" minBufferPx="60" maxBufferPx="90">
  <div cdkDropList>
    <div class="list-item" *cdkVirtualFor="let item of dataSource" cdkDrag [attr.id]="item.id">{{item.id}}</div>
  </div>
</cdk-virtual-scroll-viewport>
  1. Star dragging an element down.

Expected Behavior

A draggable item is always rendered where I keep a mouse pointer.

Actual Behavior

As new elements are being rendered to the DOM, the draggable item loses its position and either disappears or starts jumping in the list.

Environment

  • Angular: 9.0.5
  • CDK/Material: 9.2.0
  • Browser(s): Chrome
  • Operating System (e.g. Windows, macOS, Ubuntu): Windows

Demo of the problem:

2020-04-06_14-50-54

My investigation

The cause of the problem seems to be the new items that are being rendered. Once an item is added to the DOM, the CdkDrag directive attached to it registers itself within a drop list container. This in turn leads to the withItems method being triggered on the drop container list:

image (2)

As the callstack unfolds, the cacheItemPositions method is called. The problem is that inside this method all current item positions (offsets) are reset to 0. If you add a logging to the method before and after this._this._itemPositions are updated:

this._itemPositions.forEach(item => console.log('Element ID:', item.drag.data.element.nativeElement.id, 'offset:', item.offset));

this is what the console shows:

2020-04-06_14-58-16

This leads to a placeholder element lose current position and start jumping in the list.

I'm happy to make a PR to fix it, if I get guidance on the best way to address this problem.

@maxkoretskyi maxkoretskyi added the needs triage This issue needs to be triaged by the team label Apr 6, 2020
@maxkoretskyi maxkoretskyi changed the title bug(drag-drop): Draggable item loses its position when new list item is rendered in the DOM as a result of virtual scrolling bug(drag-drop + virtual scroll): Draggable item loses its position when new list item is rendered in the DOM as a result of virtual scrolling Apr 6, 2020
@crisbeto
Copy link
Member

crisbeto commented Apr 6, 2020

Using the drag&drop module together with infinite scrolling isn't supported, because we have some internal assumptions that all elements will be in the DOM. Closing the issue supporting it would require a major refactor and potentially a breaking public API change.

@crisbeto crisbeto closed this as completed Apr 6, 2020
@JoelKap
Copy link

JoelKap commented May 6, 2020

@crisbeto I am wondering when you said it isn't supported are you specifically referring to angular 9? the reason being i was able to accomplish this by the following code below:

<cdk-virtual-scroll-viewport itemSize="50" class="viewport"> <div cdkDropList (cdkDropListDropped)="onDrop($event)"> <div *cdkVirtualFor="let i of numbers" cdkDrag> {{i}} </div> </div> </cdk-virtual-scroll-viewport>

but when i used this line of code below am getting the same issue @maxkoretskyi is getting.

<cdk-virtual-scroll-viewport itemSize="20" class="viewport"> <div class="drop-list" cdkDropList [class.item-dragging]="dragging" (cdkDropListDropped)="droppedIntoList($event)"> <div *cdkVirtualFor="let item of items; let index = index" class="item" [class.selected]="isSelected(index)" cdkDrag (cdkDragStarted)="dragStarted($event, index)" (cdkDragEnded)="dragEnded()" (cdkDragDropped)="dropped($event)" (click)="select($event, index)"> <div *ngIf="!dragging || !isSelected(index)"> <ng-container *ngTemplateOutlet="templateRef; context: { $implicit: item, item: item, index: index }"> </ng-container> <div *cdkDragPreview> <div class="select-item-drag-preview float-left"> {{ selections.length || 1 }} </div> <ng-container *ngTemplateOutlet="templateRef; context: { $implicit: item, item: item, index: index }"> </ng-container> </div> </div> </div> </div> </cdk-virtual-scroll-viewport>

@mmalerba mmalerba removed the needs triage This issue needs to be triaged by the team label Jun 5, 2020
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Jul 6, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants