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

[drag-drop] cdk-drag node is detached after element is dropped from one container to another #30506

Closed
TetianaMoshon opened this issue Feb 17, 2025 · 9 comments · Fixed by #30514
Assignees
Labels
area: cdk/drag-drop P4 A relatively minor issue that is not relevant to core functions

Comments

@TetianaMoshon
Copy link

TetianaMoshon commented Feb 17, 2025

Description

If the element is dropped from one container to another, a detached node is created because of CdkDropList._dropListRef._draggables.

Reproduction

StackBlitz link: https://stackblitz.com/edit/stackblitz-starters-vygnusmw

Steps to reproduce:

  1. Download the project from StackBlitz.
  2. Start it and open in the Edge browser. Open the deprecated "Detached elements" tool.
  3. Drag an item from the "To do" column to the "In progress" column and then to "Done".
  4. Collect garbage in the "Detached elements" tool and check detached items.

Expected Behavior

There are no detached nodes related to the cdk-drag element.

Actual Behavior

There are 2 cdk-drag-dragging detached nodes.

Image

The same result will be achieved if performed in Chrome, but more details can be seen in the Edge. If the "Analyze" button is clicked - you will get an id to the retainer (instead of the initial text "Object not found in memory anymore"). Double-clicking on it will show the retainer info. In this case, it's the CdkDropList._dropListRef._draggables array.

Image

Some info after my analysis: _draggables are set in the DropListRef's withItems method.

Image

While debugging I should have entered it via _syncItemsWithRef, but there's a this._dropListRef.isDragging() condition in the removeItem method. In my case, I've already dropped an item and it's no longer dragging. Therefore _draggables are not updated and there's a cdk-drag detached node because of that.

Image

I'm not sure if that condition should be there at all, cause surely we need to sync items with ref not only when dragging is in progress. Here's a link to the commit where those changes were added.

For my project, I monkey-patched removeItem method and removed isDragging() condition. For us, the case when an item should be removed while dragging is not relevant.

Environment

  • Angular: 19.0.6
  • CDK/Material: 19.0.0
  • Browser(s): Chrome/Edge
  • Operating System (e.g. Windows, macOS, Ubuntu): Windows 11 Pro
@TetianaMoshon TetianaMoshon added the needs triage This issue needs to be triaged by the team label Feb 17, 2025
@crisbeto
Copy link
Member

I tried the component from the Stackblitz in our local dev app and I couldn't reproduce the leak on Chrome 133. I also don't think it would be particularly severe since the _draggables will be overwritten the next time dragging starts.

The context behind the isDragging check is that we don't want the items to change in the middle of dragging or things can break down.

@TetianaMoshon
Copy link
Author

@crisbeto Hello. How have you tried dragging items? Was it from container to container and not inside one? Because I can reproduce the issue in Chrome 133.0.6943.99 and Edge 133.0.3065.69.

Image

  1. What do you mean by "since the _draggables will be overwritten the next time dragging starts"? Detached nodes are already created and will not be cleaned up after garbage collection.

The provided example is simple enough, but in my app, I have a lot of containers (including nested ones) that can consist of many complex components. Hence, if such a container is moved to another and a detached node is created - it can create memory leaks.

  1. Regarding isDragging check: removeItem method is called from CdkDrag's ngOnDestroy and in my case, it's called not in the middle of dragging, but when an item is dropped.

Image

What things can break down? Can you specify what test covers it? Because I see only a test that checks if removeItem was called and nothing related to the isDragging condition.

Image

@crisbeto
Copy link
Member

Yes, I tried a few times taking an item from "Todo" and dropping it into "Done" while passing over "In progress". The only detached nodes I saw were being retained by the Chrome dev tools, rather than anything in Angular or the CDK.

What things can break down? Can you specify what test covers it? Because I see only a test that checks if removeItem was called and nothing related to the isDragging condition.

The drag-drop module measures DOM nodes when dragging starts and then modifies the cached size in memory. If the set of items changes during dragging, it can throw off those measurements and cause it to behave in weird ways. That's why there are some safeguards to prevent it from changing.

@crisbeto
Copy link
Member

crisbeto commented Feb 18, 2025

Trying to remember: I think the isDragging check is there, because the assumption is that if an item isn't being dragged, the list will be synced up before the next drag. IIRC I did it that way to avoid doing a bunch of processing up-front when the items are created for the first time. That doesn't make sense when an item is being removed.

@crisbeto crisbeto reopened this Feb 18, 2025
@TetianaMoshon
Copy link
Author

@crisbeto What detached node retained by the Chrome dev tools are you talking about? Can you please share a screen with it? It's not the same as from my screens, yes?

@crisbeto
Copy link
Member

I'm referring to these sorts of retained nodes. My understanding is that the InspectorOverlayHost is from the dev tools:

Image

@TetianaMoshon
Copy link
Author

@crisbeto I've encountered such detached nodes before, but in my case, I have a reference to _dropListRef.

Image

Also, my colleague tried to reproduce the issue and she has the same results as I. Maybe the reproduction can be related to OS? We are checking it on Windows. What about you?

@crisbeto
Copy link
Member

I'm checking it on a Mac with Chrome 133.

@crisbeto crisbeto self-assigned this Feb 19, 2025
@crisbeto crisbeto added P4 A relatively minor issue that is not relevant to core functions area: cdk/drag-drop and removed needs triage This issue needs to be triaged by the team labels Feb 19, 2025
crisbeto added a commit to crisbeto/material2 that referenced this issue Feb 19, 2025
We have some logic that registers and de-registers the items on create/destroy. That logic only syncs the item with the internal `DragRef` once the next dragging starts which means that we might retain a destroyed item if another drag doesn't start.

These changes switch to always syncing the list when an item is removed.

I've also added some context around why things are set up as they are right now since it took a while to remember the reasoning.

Fixes angular#30506.
@crisbeto
Copy link
Member

I've sent #30514 which should resolve it.

crisbeto added a commit that referenced this issue Feb 19, 2025
…30514)

We have some logic that registers and de-registers the items on create/destroy. That logic only syncs the item with the internal `DragRef` once the next dragging starts which means that we might retain a destroyed item if another drag doesn't start.

These changes switch to always syncing the list when an item is removed.

I've also added some context around why things are set up as they are right now since it took a while to remember the reasoning.

Fixes #30506.

(cherry picked from commit 26765a4)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: cdk/drag-drop P4 A relatively minor issue that is not relevant to core functions
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants