Skip to content

Commit 4e15ba9

Browse files
crisbetommalerba
authored andcommitted
fix(dialog): clean up open dialogs on destroy (#12835)
Closes all of the open dialogs when `MatDialog` is destroyed.
1 parent 2b89342 commit 4e15ba9

File tree

2 files changed

+51
-10
lines changed

2 files changed

+51
-10
lines changed

src/lib/dialog/dialog.spec.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,20 @@ describe('MatDialog', () => {
637637
expect(overlayContainerElement.querySelectorAll('mat-dialog-container').length).toBe(0);
638638
}));
639639

640+
it('should close all of the dialogs when the injectable is destroyed', fakeAsync(() => {
641+
dialog.open(PizzaMsg);
642+
dialog.open(PizzaMsg);
643+
dialog.open(PizzaMsg);
644+
645+
expect(overlayContainerElement.querySelectorAll('mat-dialog-container').length).toBe(3);
646+
647+
dialog.ngOnDestroy();
648+
viewContainerFixture.detectChanges();
649+
flush();
650+
651+
expect(overlayContainerElement.querySelectorAll('mat-dialog-container').length).toBe(0);
652+
}));
653+
640654
it('should allow the consumer to disable closing a dialog on navigation', fakeAsync(() => {
641655
dialog.open(PizzaMsg);
642656
dialog.open(PizzaMsg, {closeOnNavigation: false});
@@ -1232,6 +1246,22 @@ describe('MatDialog with a parent MatDialog', () => {
12321246

12331247
expect(overlayContainerElement.querySelector('mat-dialog-container')).toBeNull();
12341248
}));
1249+
1250+
it('should not close the parent dialogs when a child is destroyed', fakeAsync(() => {
1251+
parentDialog.open(PizzaMsg);
1252+
fixture.detectChanges();
1253+
flush();
1254+
1255+
expect(overlayContainerElement.textContent)
1256+
.toContain('Pizza', 'Expected a dialog to be opened');
1257+
1258+
childDialog.ngOnDestroy();
1259+
fixture.detectChanges();
1260+
flush();
1261+
1262+
expect(overlayContainerElement.textContent)
1263+
.toContain('Pizza', 'Expected a dialog to be opened');
1264+
}));
12351265
});
12361266

12371267
describe('MatDialog with default options', () => {

src/lib/dialog/dialog.ts

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
Injectable,
2222
InjectionToken,
2323
Injector,
24+
OnDestroy,
2425
Optional,
2526
SkipSelf,
2627
TemplateRef,
@@ -66,7 +67,7 @@ export const MAT_DIALOG_SCROLL_STRATEGY_PROVIDER = {
6667
* Service to open Material Design modal dialogs.
6768
*/
6869
@Injectable()
69-
export class MatDialog {
70+
export class MatDialog implements OnDestroy {
7071
private _openDialogsAtThisLevel: MatDialogRef<any>[] = [];
7172
private readonly _afterAllClosedAtThisLevel = new Subject<void>();
7273
private readonly _afterOpenedAtThisLevel = new Subject<MatDialogRef<any>>();
@@ -152,15 +153,7 @@ export class MatDialog {
152153
* Closes all of the currently-open dialogs.
153154
*/
154155
closeAll(): void {
155-
let i = this.openDialogs.length;
156-
157-
while (i--) {
158-
// The `_openDialogs` property isn't updated after close until the rxjs subscription
159-
// runs on the next microtask, in addition to modifying the array as we're going
160-
// through it. We loop through all of them and call close without assuming that
161-
// they'll be removed from the list instantaneously.
162-
this.openDialogs[i].close();
163-
}
156+
this._closeDialogs(this.openDialogs);
164157
}
165158

166159
/**
@@ -171,6 +164,12 @@ export class MatDialog {
171164
return this.openDialogs.find(dialog => dialog.id === id);
172165
}
173166

167+
ngOnDestroy() {
168+
// Only close the dialogs at this level on destroy
169+
// since the parent service may still be active.
170+
this._closeDialogs(this._openDialogsAtThisLevel);
171+
}
172+
174173
/**
175174
* Creates the overlay into which the dialog will be loaded.
176175
* @param config The dialog configuration.
@@ -357,7 +356,19 @@ export class MatDialog {
357356
}
358357
}
359358
}
359+
}
360+
361+
/** Closes all of the dialogs in an array. */
362+
private _closeDialogs(dialogs: MatDialogRef<any>[]) {
363+
let i = dialogs.length;
360364

365+
while (i--) {
366+
// The `_openDialogs` property isn't updated after close until the rxjs subscription
367+
// runs on the next microtask, in addition to modifying the array as we're going
368+
// through it. We loop through all of them and call close without assuming that
369+
// they'll be removed from the list instantaneously.
370+
dialogs[i].close();
371+
}
361372
}
362373

363374
}

0 commit comments

Comments
 (0)