9
9
import {
10
10
ANIMATION_MODULE_TYPE ,
11
11
ChangeDetectionStrategy ,
12
+ ChangeDetectorRef ,
12
13
Component ,
13
14
ComponentRef ,
15
+ DoCheck ,
14
16
ElementRef ,
15
17
EmbeddedViewRef ,
16
18
inject ,
@@ -53,16 +55,18 @@ import {MatSnackBarConfig} from './snack-bar-config';
53
55
'(animationend)' : '_animationDone($event)' ,
54
56
} ,
55
57
} )
56
- export class MatSnackBarContainer extends BasePortalOutlet implements OnDestroy {
58
+ export class MatSnackBarContainer extends BasePortalOutlet implements OnDestroy , DoCheck {
57
59
private _ngZone = inject ( NgZone ) ;
58
60
private _elementRef = inject < ElementRef < HTMLElement > > ( ElementRef ) ;
59
61
private _platform = inject ( Platform ) ;
62
+ private _changeDetectorRef = inject ( ChangeDetectorRef ) ;
60
63
private _enterFallback : ReturnType < typeof setTimeout > | undefined ;
61
64
private _exitFallback : ReturnType < typeof setTimeout > | undefined ;
62
65
protected _animationsDisabled =
63
66
inject ( ANIMATION_MODULE_TYPE , { optional : true } ) === 'NoopAnimations' ;
64
- snackBarConfig = inject ( MatSnackBarConfig ) ;
67
+ private _scheduleDelayedEnter : boolean ;
65
68
69
+ snackBarConfig = inject ( MatSnackBarConfig ) ;
66
70
private _document = inject ( DOCUMENT ) ;
67
71
private _trackedModals = new Set < Element > ( ) ;
68
72
@@ -174,15 +178,23 @@ export class MatSnackBarContainer extends BasePortalOutlet implements OnDestroy
174
178
/** Begin animation of snack bar entrance into view. */
175
179
enter ( ) : void {
176
180
if ( ! this . _destroyed ) {
181
+ // Previously this was used to ensure that the change-detection-based animation runs.
182
+ // Now the animation doesn't require change detection, but there seem to be some internal
183
+ // usages depending on it.
184
+ this . _changeDetectorRef . markForCheck ( ) ;
185
+ this . _changeDetectorRef . detectChanges ( ) ;
177
186
this . _screenReaderAnnounce ( ) ;
178
187
179
188
if ( this . _animationsDisabled ) {
180
- this . _completeEnter ( ) ;
189
+ // Delay the enter until the next change detection in an attempt to mimic the timing of
190
+ // the old animation-based events. Ideally we would use an `afterNextRender` here, but
191
+ // some tests throw a "Injector has already been destroyed" error.
192
+ this . _scheduleDelayedEnter = true ;
181
193
} else {
182
194
// Guarantees that the animation-related events will
183
195
// fire even if something interrupts the animation.
184
196
clearTimeout ( this . _enterFallback ) ;
185
- this . _enterFallback = setTimeout ( this . _completeEnter , 200 ) ;
197
+ this . _enterFallback = setTimeout ( ( ) => this . _completeEnter ( ) , 200 ) ;
186
198
}
187
199
}
188
200
}
@@ -194,51 +206,54 @@ export class MatSnackBarContainer extends BasePortalOutlet implements OnDestroy
194
206
// test harness.
195
207
this . _elementRef . nativeElement . setAttribute ( 'mat-exit' , '' ) ;
196
208
197
- // If the snack bar hasn't been announced by the time it exits it wouldn't have been open
198
- // long enough to visually read it either, so clear the timeout for announcing.
199
- clearTimeout ( this . _announceTimeoutId ) ;
200
-
201
- if ( this . _animationsDisabled ) {
202
- // It's common for snack bars to be opened by random outside calls like HTTP requests or
203
- // errors. Run inside the NgZone to ensure that it functions correctly.
204
- this . _ngZone . run ( this . _completeExit ) ;
205
- } else {
206
- // Guarantees that the animation-related events will
207
- // fire even if something interrupts the animation.
208
- clearTimeout ( this . _exitFallback ) ;
209
- this . _exitFallback = setTimeout ( this . _completeExit , 150 ) ;
210
- }
209
+ // Guarantees that the animation-related events will
210
+ // fire even if something interrupts the animation.
211
+ clearTimeout ( this . _exitFallback ) ;
212
+ this . _exitFallback = setTimeout (
213
+ ( ) => this . _completeExit ( ) ,
214
+ this . _animationsDisabled ? undefined : 150 ,
215
+ ) ;
211
216
212
217
return this . _onExit ;
213
218
}
214
219
215
- /** Makes sure the exit callbacks have been invoked when the element is destroyed. */
220
+ ngDoCheck ( ) : void {
221
+ if ( this . _scheduleDelayedEnter ) {
222
+ this . _scheduleDelayedEnter = false ;
223
+ this . _completeEnter ( ) ;
224
+ }
225
+ }
226
+
216
227
ngOnDestroy ( ) {
217
228
clearTimeout ( this . _enterFallback ) ;
218
229
this . _destroyed = true ;
219
230
this . _clearFromModals ( ) ;
220
231
this . _completeExit ( ) ;
232
+ this . _onAnnounce . complete ( ) ;
221
233
}
222
234
223
- private _completeEnter = ( ) => {
235
+ private _completeEnter ( ) {
224
236
clearTimeout ( this . _enterFallback ) ;
225
237
this . _ngZone . run ( ( ) => {
226
238
this . _onEnter . next ( ) ;
227
239
this . _onEnter . complete ( ) ;
228
240
} ) ;
229
- } ;
241
+ }
230
242
231
243
/**
232
244
* Removes the element in a microtask. Helps prevent errors where we end up
233
245
* removing an element which is in the middle of an animation.
234
246
*/
235
- private _completeExit = ( ) => {
247
+ private _completeExit ( ) {
248
+ // If the snack bar hasn't been announced by the time it exits it wouldn't have been open
249
+ // long enough to visually read it either, so clear the timeout for announcing.
250
+ clearTimeout ( this . _announceTimeoutId ) ;
236
251
clearTimeout ( this . _exitFallback ) ;
237
- queueMicrotask ( ( ) => {
252
+ this . _ngZone . run ( ( ) => {
238
253
this . _onExit . next ( ) ;
239
254
this . _onExit . complete ( ) ;
240
255
} ) ;
241
- } ;
256
+ }
242
257
243
258
/**
244
259
* Called after the portal contents have been attached. Can be
0 commit comments