Skip to content

Commit 730e7ae

Browse files
crisbetojelbourn
authored andcommitted
fix(snack-bar): animation not starting for subsequent snack bars (#6649)
Since the switch to OnPush change detection, opening a snack bar while another one is open, programmatically isn't guaranteed to actually show the second snack bar, because the user might not hit the zone (e.g. if it's through a timeout). These changes trigger change detection manually on open. I also got rid of a couple of redundant methods on the snack bar container. Fixes #6222.
1 parent 89fea50 commit 730e7ae

File tree

2 files changed

+20
-26
lines changed

2 files changed

+20
-26
lines changed

src/lib/snack-bar/snack-bar-container.ts

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
ElementRef,
1818
ChangeDetectionStrategy,
1919
ViewEncapsulation,
20+
ChangeDetectorRef,
2021
} from '@angular/core';
2122
import {
2223
trigger,
@@ -63,24 +64,25 @@ export const HIDE_ANIMATION = '195ms cubic-bezier(0.0,0.0,0.2,1)';
6364
},
6465
animations: [
6566
trigger('state', [
66-
state('void', style({transform: 'translateY(100%)'})),
67-
state('initial', style({transform: 'translateY(100%)'})),
67+
state('void, initial, complete', style({transform: 'translateY(100%)'})),
6868
state('visible', style({transform: 'translateY(0%)'})),
69-
state('complete', style({transform: 'translateY(100%)'})),
7069
transition('visible => complete', animate(HIDE_ANIMATION)),
7170
transition('initial => visible, void => visible', animate(SHOW_ANIMATION)),
7271
])
7372
],
7473
})
7574
export class MdSnackBarContainer extends BasePortalHost implements OnDestroy {
75+
/** Whether the component has been destroyed. */
76+
private _destroyed = false;
77+
7678
/** The portal host inside of this container into which the snack bar content will be loaded. */
7779
@ViewChild(PortalHostDirective) _portalHost: PortalHostDirective;
7880

7981
/** Subject for notifying that the snack bar has exited from view. */
80-
private onExit: Subject<any> = new Subject();
82+
_onExit: Subject<any> = new Subject();
8183

8284
/** Subject for notifying that the snack bar has finished entering the view. */
83-
private onEnter: Subject<any> = new Subject();
85+
_onEnter: Subject<any> = new Subject();
8486

8587
/** The state of the snack bar animations. */
8688
animationState: SnackBarState = 'initial';
@@ -91,7 +93,8 @@ export class MdSnackBarContainer extends BasePortalHost implements OnDestroy {
9193
constructor(
9294
private _ngZone: NgZone,
9395
private _renderer: Renderer2,
94-
private _elementRef: ElementRef) {
96+
private _elementRef: ElementRef,
97+
private _changeDetectorRef: ChangeDetectorRef) {
9598
super();
9699
}
97100

@@ -126,7 +129,7 @@ export class MdSnackBarContainer extends BasePortalHost implements OnDestroy {
126129
if (event.toState === 'visible') {
127130
// Note: we shouldn't use `this` inside the zone callback,
128131
// because it can cause a memory leak.
129-
const onEnter = this.onEnter;
132+
const onEnter = this._onEnter;
130133

131134
this._ngZone.run(() => {
132135
onEnter.next();
@@ -137,30 +140,21 @@ export class MdSnackBarContainer extends BasePortalHost implements OnDestroy {
137140

138141
/** Begin animation of snack bar entrance into view. */
139142
enter(): void {
140-
this.animationState = 'visible';
141-
}
142-
143-
/** Returns an observable resolving when the enter animation completes. */
144-
_onEnter(): Observable<void> {
145-
this.animationState = 'visible';
146-
return this.onEnter.asObservable();
143+
if (!this._destroyed) {
144+
this.animationState = 'visible';
145+
this._changeDetectorRef.detectChanges();
146+
}
147147
}
148148

149149
/** Begin animation of the snack bar exiting from view. */
150150
exit(): Observable<void> {
151151
this.animationState = 'complete';
152-
return this._onExit();
152+
return this._onExit;
153153
}
154154

155-
/** Returns an observable that completes after the closing animation is done. */
156-
_onExit(): Observable<void> {
157-
return this.onExit.asObservable();
158-
}
159-
160-
/**
161-
* Makes sure the exit callbacks have been invoked when the element is destroyed.
162-
*/
155+
/** Makes sure the exit callbacks have been invoked when the element is destroyed. */
163156
ngOnDestroy() {
157+
this._destroyed = true;
164158
this._completeExit();
165159
}
166160

@@ -171,7 +165,7 @@ export class MdSnackBarContainer extends BasePortalHost implements OnDestroy {
171165
private _completeExit() {
172166
// Note: we shouldn't use `this` inside the zone callback,
173167
// because it can cause a memory leak.
174-
const onExit = this.onExit;
168+
const onExit = this._onExit;
175169

176170
first.call(this._ngZone.onMicrotaskEmpty).subscribe(() => {
177171
onExit.next();

src/lib/snack-bar/snack-bar-ref.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export class MdSnackBarRef<T> {
4444
this.containerInstance = containerInstance;
4545
// Dismiss snackbar on action.
4646
this.onAction().subscribe(() => this.dismiss());
47-
containerInstance._onExit().subscribe(() => this._finishDismiss());
47+
containerInstance._onExit.subscribe(() => this._finishDismiss());
4848
}
4949

5050
/** Dismisses the snack bar. */
@@ -90,7 +90,7 @@ export class MdSnackBarRef<T> {
9090

9191
/** Gets an observable that is notified when the snack bar has opened and appeared. */
9292
afterOpened(): Observable<void> {
93-
return this.containerInstance._onEnter();
93+
return this.containerInstance._onEnter;
9494
}
9595

9696
/** Gets an observable that is notified when the snack bar action is called. */

0 commit comments

Comments
 (0)