mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(drag-drop): DOM element not being returned to initial container on drop (#12948)
Currently when an item is dropped, we return it to its initial position in the DOM so Angular can take over and re-render it if necessary, however this breaks down if the item is the only child of the drop container and we weren't able to grab a reference element that we can use to restore its position. These changes address the issue by using the initial drop container as a reference. Fixes #12944.
This commit is contained in:
committed by
Jeremy Elbourn
parent
d1c27ae1c4
commit
fb7bf900c0
@@ -1435,6 +1435,45 @@ describe('CdkDrag', () => {
|
||||
});
|
||||
}));
|
||||
|
||||
it('should return DOM element to its initial container after it is dropped, in a container ' +
|
||||
'with one draggable item', fakeAsync(() => {
|
||||
const fixture = createComponent(ConnectedDropZonesWithSingleItems);
|
||||
fixture.detectChanges();
|
||||
|
||||
const items = fixture.componentInstance.dragItems.toArray();
|
||||
const item = items[0];
|
||||
const targetRect = items[1].element.nativeElement.getBoundingClientRect();
|
||||
const dropContainers = fixture.componentInstance.dropInstances
|
||||
.map(drop => drop.element.nativeElement);
|
||||
|
||||
expect(dropContainers[0].contains(item.element.nativeElement)).toBe(true,
|
||||
'Expected DOM element to be in first container');
|
||||
expect(item.dropContainer).toBe(fixture.componentInstance.dropInstances.first,
|
||||
'Expected CdkDrag to be in first container in memory');
|
||||
|
||||
dragElementViaMouse(fixture, item.element.nativeElement,
|
||||
targetRect.left + 1, targetRect.top + 1);
|
||||
flush();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.componentInstance.droppedSpy).toHaveBeenCalledTimes(1);
|
||||
|
||||
const event = fixture.componentInstance.droppedSpy.calls.mostRecent().args[0];
|
||||
|
||||
expect(event).toEqual({
|
||||
previousIndex: 0,
|
||||
currentIndex: 0,
|
||||
item,
|
||||
container: fixture.componentInstance.dropInstances.toArray()[1],
|
||||
previousContainer: fixture.componentInstance.dropInstances.first
|
||||
});
|
||||
|
||||
expect(dropContainers[0].contains(item.element.nativeElement)).toBe(true,
|
||||
'Expected DOM element to be returned to first container');
|
||||
expect(item.dropContainer).toBe(fixture.componentInstance.dropInstances.first,
|
||||
'Expected CdkDrag to be returned to first container in memory');
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1681,6 +1720,39 @@ class DraggableWithAlternateRoot {
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
styles: [`
|
||||
.cdk-drop {
|
||||
display: block;
|
||||
width: 100px;
|
||||
min-height: ${ITEM_HEIGHT}px;
|
||||
background: hotpink;
|
||||
}
|
||||
|
||||
.cdk-drag {
|
||||
display: block;
|
||||
height: ${ITEM_HEIGHT}px;
|
||||
background: red;
|
||||
}
|
||||
`],
|
||||
template: `
|
||||
<cdk-drop #todoZone [connectedTo]="[doneZone]" (dropped)="droppedSpy($event)">
|
||||
<div cdkDrag>One</div>
|
||||
</cdk-drop>
|
||||
|
||||
<cdk-drop #doneZone [connectedTo]="[todoZone]" (dropped)="droppedSpy($event)">
|
||||
<div cdkDrag>Two</div>
|
||||
</cdk-drop>
|
||||
`
|
||||
})
|
||||
class ConnectedDropZonesWithSingleItems {
|
||||
@ViewChildren(CdkDrag) dragItems: QueryList<CdkDrag>;
|
||||
@ViewChildren(CdkDrop) dropInstances: QueryList<CdkDrop>;
|
||||
|
||||
droppedSpy = jasmine.createSpy('dropped spy');
|
||||
}
|
||||
|
||||
/**
|
||||
* Drags an element to a position on the page using the mouse.
|
||||
* @param fixture Fixture on which to run change detection.
|
||||
|
||||
@@ -396,7 +396,7 @@ export class CdkDrag<T = any> implements AfterViewInit, OnDestroy {
|
||||
if (this._nextSibling) {
|
||||
this._nextSibling.parentNode!.insertBefore(this._rootElement, this._nextSibling);
|
||||
} else {
|
||||
this._placeholder.parentNode!.appendChild(this._rootElement);
|
||||
this._initialContainer.element.nativeElement.appendChild(this._rootElement);
|
||||
}
|
||||
|
||||
this._destroyPreview();
|
||||
@@ -415,6 +415,7 @@ export class CdkDrag<T = any> implements AfterViewInit, OnDestroy {
|
||||
previousContainer: this._initialContainer
|
||||
});
|
||||
this.dropContainer.drop(this, currentIndex, this._initialContainer);
|
||||
this.dropContainer = this._initialContainer;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -6,11 +6,14 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {InjectionToken, QueryList} from '@angular/core';
|
||||
import {InjectionToken, QueryList, ElementRef} from '@angular/core';
|
||||
import {CdkDrag} from './drag';
|
||||
|
||||
|
||||
export interface CdkDropContainer<T = any> {
|
||||
/** DOM node that corresponds to the drop container. */
|
||||
element: ElementRef<HTMLElement>;
|
||||
|
||||
/** Arbitrary data to attach to all events emitted by this container. */
|
||||
data: T;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user