Skip to content

Commit 57c68e8

Browse files
authored
fix(material-experimental/mdc-dialog): afterClosed being run outside of NgZone (#21702)
Fixes that all of the callbacks tied to animations within the MDC-based dialog were being run outside of the `NgZone`. Fixes #21696.
1 parent da1f7e7 commit 57c68e8

File tree

3 files changed

+41
-4
lines changed

3 files changed

+41
-4
lines changed

src/material-experimental/mdc-dialog/dialog-container.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import {
1414
Component,
1515
ElementRef,
1616
Inject,
17-
NgZone,
1817
OnDestroy,
1918
Optional,
2019
ViewEncapsulation
@@ -66,7 +65,6 @@ export class MatDialogContainer extends _MatDialogContainerBase implements OnDes
6665
changeDetectorRef: ChangeDetectorRef,
6766
@Optional() @Inject(DOCUMENT) document: any,
6867
config: MatDialogConfig,
69-
private _ngZone: NgZone,
7068
@Optional() @Inject(ANIMATION_MODULE_TYPE) private _animationMode?: string,
7169
focusMonitor?: FocusMonitor) {
7270
super(elementRef, focusTrapFactory, changeDetectorRef, document, config, focusMonitor);
@@ -180,6 +178,9 @@ export class MatDialogContainer extends _MatDialogContainerBase implements OnDes
180178
if (this._animationTimer !== null) {
181179
clearTimeout(this._animationTimer);
182180
}
183-
this._ngZone.runOutsideAngular(() => this._animationTimer = setTimeout(callback, duration));
181+
182+
// Note that we want this timer to run inside the NgZone, because we want
183+
// the related events like `afterClosed` to be inside the zone as well.
184+
this._animationTimer = setTimeout(callback, duration);
184185
}
185186
}

src/material-experimental/mdc-dialog/dialog.spec.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
Inject,
2121
Injector,
2222
NgModule,
23+
NgZone,
2324
TemplateRef,
2425
ViewChild,
2526
ViewContainerRef
@@ -201,6 +202,23 @@ describe('MDC-based MatDialog', () => {
201202
expect(overlayContainerElement.querySelector('mat-mdc-dialog-container')).toBeNull();
202203
}));
203204

205+
it('should invoke the afterClosed callback inside the NgZone',
206+
fakeAsync(inject([NgZone], (zone: NgZone) => {
207+
const dialogRef = dialog.open(PizzaMsg, { viewContainerRef: testViewContainerRef });
208+
const afterCloseCallback = jasmine.createSpy('afterClose callback');
209+
210+
dialogRef.afterClosed().subscribe(() => {
211+
afterCloseCallback(NgZone.isInAngularZone());
212+
});
213+
zone.run(() => {
214+
dialogRef.close();
215+
viewContainerFixture.detectChanges();
216+
flush();
217+
});
218+
219+
expect(afterCloseCallback).toHaveBeenCalledWith(true);
220+
})));
221+
204222
it('should dispose of dialog if view container is destroyed while animating', fakeAsync(() => {
205223
const dialogRef = dialog.open(PizzaMsg, {viewContainerRef: testViewContainerRef});
206224

src/material/dialog/dialog.spec.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ import {
1818
TemplateRef,
1919
ViewChild,
2020
ViewContainerRef,
21-
ComponentFactoryResolver
21+
ComponentFactoryResolver,
22+
NgZone
2223
} from '@angular/core';
2324
import {By} from '@angular/platform-browser';
2425
import {BrowserAnimationsModule, NoopAnimationsModule} from '@angular/platform-browser/animations';
@@ -204,6 +205,23 @@ describe('MatDialog', () => {
204205
expect(overlayContainerElement.querySelector('mat-dialog-container')).toBeNull();
205206
}));
206207

208+
it('should invoke the afterClosed callback inside the NgZone',
209+
fakeAsync(inject([NgZone], (zone: NgZone) => {
210+
const dialogRef = dialog.open(PizzaMsg, { viewContainerRef: testViewContainerRef });
211+
const afterCloseCallback = jasmine.createSpy('afterClose callback');
212+
213+
dialogRef.afterClosed().subscribe(() => {
214+
afterCloseCallback(NgZone.isInAngularZone());
215+
});
216+
zone.run(() => {
217+
dialogRef.close();
218+
viewContainerFixture.detectChanges();
219+
flush();
220+
});
221+
222+
expect(afterCloseCallback).toHaveBeenCalledWith(true);
223+
})));
224+
207225
it('should dispose of dialog if view container is destroyed while animating', fakeAsync(() => {
208226
const dialogRef = dialog.open(PizzaMsg, {viewContainerRef: testViewContainerRef});
209227

0 commit comments

Comments
 (0)