diff --git a/src/material-experimental/mdc-snack-bar/snack-bar-container.ts b/src/material-experimental/mdc-snack-bar/snack-bar-container.ts index a8a157561a63..afdc9fc72a77 100644 --- a/src/material-experimental/mdc-snack-bar/snack-bar-container.ts +++ b/src/material-experimental/mdc-snack-bar/snack-bar-container.ts @@ -176,18 +176,24 @@ export class MatSnackBarContainer enter() { // MDC uses some browser APIs that will throw during server-side rendering. if (this._platform.isBrowser) { - this._mdcFoundation.open(); - this._screenReaderAnnounce(); + this._ngZone.run(() => { + this._mdcFoundation.open(); + this._screenReaderAnnounce(); + }); } } exit(): Observable { - this._exiting = true; - this._mdcFoundation.close(); - - // If the snack bar hasn't been announced by the time it exits it wouldn't have been open - // long enough to visually read it either, so clear the timeout for announcing. - clearTimeout(this._announceTimeoutId); + // It's common for snack bars to be opened by random outside calls like HTTP requests or + // errors. Run inside the NgZone to ensure that it functions correctly. + this._ngZone.run(() => { + this._exiting = true; + this._mdcFoundation.close(); + + // If the snack bar hasn't been announced by the time it exits it wouldn't have been open + // long enough to visually read it either, so clear the timeout for announcing. + clearTimeout(this._announceTimeoutId); + }); return this._onExit; } diff --git a/src/material/snack-bar/snack-bar-container.ts b/src/material/snack-bar/snack-bar-container.ts index 2bf4a2d901d5..f405dd61ddd4 100644 --- a/src/material/snack-bar/snack-bar-container.ts +++ b/src/material/snack-bar/snack-bar-container.ts @@ -194,19 +194,23 @@ export class MatSnackBarContainer /** Begin animation of the snack bar exiting from view. */ exit(): Observable { - // Note: this one transitions to `hidden`, rather than `void`, in order to handle the case - // where multiple snack bars are opened in quick succession (e.g. two consecutive calls to - // `MatSnackBar.open`). - this._animationState = 'hidden'; - - // Mark this element with an 'exit' attribute to indicate that the snackbar has - // been dismissed and will soon be removed from the DOM. This is used by the snackbar - // test harness. - this._elementRef.nativeElement.setAttribute('mat-exit', ''); - - // If the snack bar hasn't been announced by the time it exits it wouldn't have been open - // long enough to visually read it either, so clear the timeout for announcing. - clearTimeout(this._announceTimeoutId); + // It's common for snack bars to be opened by random outside calls like HTTP requests or + // errors. Run inside the NgZone to ensure that it functions correctly. + this._ngZone.run(() => { + // Note: this one transitions to `hidden`, rather than `void`, in order to handle the case + // where multiple snack bars are opened in quick succession (e.g. two consecutive calls to + // `MatSnackBar.open`). + this._animationState = 'hidden'; + + // Mark this element with an 'exit' attribute to indicate that the snackbar has + // been dismissed and will soon be removed from the DOM. This is used by the snackbar + // test harness. + this._elementRef.nativeElement.setAttribute('mat-exit', ''); + + // If the snack bar hasn't been announced by the time it exits it wouldn't have been open + // long enough to visually read it either, so clear the timeout for announcing. + clearTimeout(this._announceTimeoutId); + }); return this._onExit; } @@ -223,8 +227,10 @@ export class MatSnackBarContainer */ private _completeExit() { this._ngZone.onMicrotaskEmpty.pipe(take(1)).subscribe(() => { - this._onExit.next(); - this._onExit.complete(); + this._ngZone.run(() => { + this._onExit.next(); + this._onExit.complete(); + }); }); }