Components: [Drag and drop] problems when changing height after start

Created on 2 Jan 2019  路  9Comments  路  Source: angular/components

What is the expected behavior?

Drag and drop working :)

What is the current behavior?

If I change the height of elements (that are draggable) for example by hiding some elements after I start dragging, the drag and drop mechanism thinks the items are still the original height (and therefore rendering placeholder at wrong place and behaving incorrectly).

What are the steps to reproduce?

https://stackblitz.com/edit/angular-material2-issue-ggdtyr?file=app%2Fapp.component.css

(moving between columns does not work for some unknown reason but that is not important)

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

newest ones

Is there anything else we should know?

If you point me in the right direction, I can provide a fix.

P2 cddrag-drop

Most helpful comment

Bumping to a P2 since it seems like an issue that people keep bumping into, based on the number of duplicate issues.

All 9 comments

@JurajMlich A temporal hack fix to that issue, on the cdkDragStarted event i put this, it is not a nice solution but meanwhile it is fixed.

  dragStarted(event: {source: CdkDrag}) {
    setTimeout(() => {
      event.source._dragRef.dropContainer['_cachePositions']();
    }, 200);
  }

@JurajMlich I tried to attempt the same thing. I just hide content inside of draggable elements when an element starts dragging.
.cdk-drop-list-dragging { mat-card { mat-card-content { display: none; } } }

@sergiocosus I am getting an error when I tried your solution.

Property 'dropContainer' does not exist on type 'DragRef<CdkDrag<any>>'. Did you mean '_dropContainer'?ts(2551)
drag-ref.d.ts(137, 13): '_dropContainer' is declared here.

@vorachirag it worked before the update. Now this works:

dragStarted(event: {source: CdkDrag}) {
  setTimeout(() => {
    const dropContainer = event.source._dragRef['_dropContainer'];

    if (dropContainer) {
      dropContainer['_cacheOwnPosition']();
      dropContainer['_cacheItemPositions']();
    }
  });
}

@thabalija Thanks. It works.

Bumping to a P2 since it seems like an issue that people keep bumping into, based on the number of duplicate issues.

There are a variety of problems going on with this issue and those recently closed.

One of which is that connected drop list client rects are not being cached after an action occurs that could affect the rendered layout (e.g. transferring an item between lists with no fixed size).

This problem can be alleviated with something like the following:
```ts
// drop-list-ref.ts
enter(item: DragRef, pointerX: number, pointerY: number, index?: number): void {
this.start();

// ...add item to this drop list's draggables 

// Note that the positions were already cached when we called `start` above,
// but we need to refresh them since the amount of items has changed and also parent rects.
this._cacheItemPositions();
this._cacheParentPositions();

// NEW - remeasure connected drop list client rects
this._siblings.forEach(sibling => {
    sibling._clientRect = sibling.element.getBoundingClientRect();
});

this.entered.next({item, container: this, currentIndex: this.getItemIndex(item)});

}
```
(though obviously not using private variables directly and perhaps setting up a public method to re-cache the drop list client rect)

Example of problem (StackBlitz):
GIF2

With above solution (uncomment lines 108-110):
GIF3


The other main issue I see, which is going to require a good deal of refactoring to fix, is that this library's functionality completely starts to fall apart once you start adding layout changing CSS styles to the classes added by dynamic host bindings (e.g. .cdk-drop-list-dragging). These classes being added/removed require change detection to run, which is an awful dependency for a general purpose library like this, and change detection is generally running after rects have been cached, causing all these measurements to [potentially] be invalidated and never refreshed. I talked about this a little more here.

@crisbeto any news on how to manage this case simply? I'm trying to drag and drop an element on an area that is collapsed and uncollapsed on hovering but it's really tedious.

I have a similar issue with my elements. They have a dynamic height but it should not change while dragging. However the placeholder height seems to be wrong as it will overlap the remaining elements (it's correctly visible but height seems to be smaller than real height resulting in the overlap)

@crisbeto any news on how to manage this case simply? I'm trying to drag and drop an element on an area that is collapsed and uncollapsed on hovering but it's really tedious.

I have literally the same problem. Is there any fix or workaround for this?

Edit: I've tried @Achilles1515's solution, it will work, but I need to trigger it at a specific condition. How can I do that?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jelbourn picture jelbourn  路  3Comments

3mp3ri0r picture 3mp3ri0r  路  3Comments

dzrust picture dzrust  路  3Comments

Miiekeee picture Miiekeee  路  3Comments

theunreal picture theunreal  路  3Comments