mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(bottom-sheet): DOM nodes not cleaned up if view container is destroyed mid-animation (#16349)
Along the same lines as #16309. Currently the bottom sheet's cleanup logic is tied to its exit animation completing. This usually isn't a problem since by default the bottom sheet is attached to the application ref, however if the consumer has set a viewContainerRef and that ref is destroyed mid-animation, the exit animation event will never fire. These changes add a timeout as a fallback in case the animation doesn't finish in the specified time.
This commit is contained in:
committed by
Andrew Seguin
parent
86aad590f1
commit
3fb87d799c
@@ -39,6 +39,9 @@ export class MatBottomSheetRef<T = any, R = any> {
|
||||
/** Result to be passed down to the `afterDismissed` stream. */
|
||||
private _result: R | undefined;
|
||||
|
||||
/** Handle to the timeout that's running as a fallback in case the exit animation doesn't fire. */
|
||||
private _closeFallbackTimeout: number;
|
||||
|
||||
constructor(
|
||||
containerInstance: MatBottomSheetContainer,
|
||||
private _overlayRef: OverlayRef,
|
||||
@@ -61,6 +64,7 @@ export class MatBottomSheetRef<T = any, R = any> {
|
||||
containerInstance._animationStateChanged
|
||||
.pipe(filter(event => event.phaseName === 'done' && event.toState === 'hidden'), take(1))
|
||||
.subscribe(() => {
|
||||
clearTimeout(this._closeFallbackTimeout);
|
||||
_overlayRef.dispose();
|
||||
});
|
||||
|
||||
@@ -91,7 +95,18 @@ export class MatBottomSheetRef<T = any, R = any> {
|
||||
this.containerInstance._animationStateChanged.pipe(
|
||||
filter(event => event.phaseName === 'start'),
|
||||
take(1)
|
||||
).subscribe(() => this._overlayRef.detachBackdrop());
|
||||
).subscribe(event => {
|
||||
// The logic that disposes of the overlay depends on the exit animation completing, however
|
||||
// it isn't guaranteed if the parent view is destroyed while it's running. Add a fallback
|
||||
// timeout which will clean everything up if the animation hasn't fired within the specified
|
||||
// amount of time plus 100ms. We don't need to run this outside the NgZone, because for the
|
||||
// vast majority of cases the timeout will have been cleared before it has fired.
|
||||
this._closeFallbackTimeout = setTimeout(() => {
|
||||
this._overlayRef.dispose();
|
||||
}, event.totalTime + 100);
|
||||
|
||||
this._overlayRef.detachBackdrop();
|
||||
});
|
||||
|
||||
this._result = result;
|
||||
this.containerInstance.exit();
|
||||
|
||||
@@ -192,6 +192,18 @@ describe('MatBottomSheet', () => {
|
||||
expect(overlayContainerElement.querySelector('mat-bottom-sheet-container')).toBeFalsy();
|
||||
}));
|
||||
|
||||
it('should dispose of bottom sheet if view container is destroyed while animating',
|
||||
fakeAsync(() => {
|
||||
const bottomSheetRef = bottomSheet.open(PizzaMsg, {viewContainerRef: testViewContainerRef});
|
||||
|
||||
bottomSheetRef.dismiss();
|
||||
viewContainerFixture.detectChanges();
|
||||
viewContainerFixture.destroy();
|
||||
flush();
|
||||
|
||||
expect(overlayContainerElement.querySelector('mat-dialog-container')).toBeNull();
|
||||
}));
|
||||
|
||||
it('should emit the backdropClick stream when clicking on the overlay backdrop', fakeAsync(() => {
|
||||
const bottomSheetRef = bottomSheet.open(PizzaMsg, {viewContainerRef: testViewContainerRef});
|
||||
const spy = jasmine.createSpy('backdropClick spy');
|
||||
|
||||
Reference in New Issue
Block a user