Skip to content

Commit 3ef933a

Browse files
crisbetojosephperrott
authored andcommitted
fix(stepper): emitting the animationDone event twice on some browsers (#13608)
Along the same lines as #13600 and #13587. The `animationDone` will be emitted twice in a row for some browsers due to a bug in `@angular/animations`.
1 parent 16098a7 commit 3ef933a

File tree

3 files changed

+19
-10
lines changed

3 files changed

+19
-10
lines changed

src/lib/stepper/stepper-horizontal.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
<div *ngFor="let step of _steps; let i = index"
2929
class="mat-horizontal-stepper-content" role="tabpanel"
3030
[@stepTransition]="_getAnimationDirection(i)"
31-
(@stepTransition.done)="_animationDone($event)"
31+
(@stepTransition.done)="_animationDone.next($event)"
3232
[id]="_getStepContentId(i)"
3333
[attr.aria-labelledby]="_getStepLabelId(i)"
3434
[attr.aria-expanded]="selectedIndex === i">

src/lib/stepper/stepper-vertical.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
<div class="mat-vertical-content-container" [class.mat-stepper-vertical-line]="!isLast">
2424
<div class="mat-vertical-stepper-content" role="tabpanel"
2525
[@stepTransition]="_getAnimationDirection(i)"
26-
(@stepTransition.done)="_animationDone($event)"
26+
(@stepTransition.done)="_animationDone.next($event)"
2727
[id]="_getStepContentId(i)"
2828
[attr.aria-labelledby]="_getStepLabelId(i)"
2929
[attr.aria-expanded]="selectedIndex === i">

src/lib/stepper/stepper.ts

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ import {
3939
import {FormControl, FormGroupDirective, NgForm} from '@angular/forms';
4040
import {DOCUMENT} from '@angular/common';
4141
import {ErrorStateMatcher} from '@angular/material/core';
42-
import {takeUntil} from 'rxjs/operators';
42+
import {Subject} from 'rxjs';
43+
import {takeUntil, distinctUntilChanged} from 'rxjs/operators';
4344

4445
import {MatStepHeader} from './step-header';
4546
import {MatStepLabel} from './step-label';
@@ -102,18 +103,26 @@ export class MatStepper extends _CdkStepper implements AfterContentInit {
102103
/** Consumer-specified template-refs to be used to override the header icons. */
103104
_iconOverrides: {[key: string]: TemplateRef<MatStepperIconContext>} = {};
104105

106+
/** Stream of animation `done` events when the body expands/collapses. */
107+
_animationDone = new Subject<AnimationEvent>();
108+
105109
ngAfterContentInit() {
106-
const icons = this._icons.toArray();
107-
icons.forEach(({name, templateRef}) => this._iconOverrides[name] = templateRef);
110+
this._icons.forEach(({name, templateRef}) => this._iconOverrides[name] = templateRef);
108111

109112
// Mark the component for change detection whenever the content children query changes
110113
this._steps.changes.pipe(takeUntil(this._destroyed)).subscribe(() => this._stateChanged());
111-
}
112114

113-
_animationDone(event: AnimationEvent) {
114-
if ((event.toState as StepContentPositionState) === 'current') {
115-
this.animationDone.emit();
116-
}
115+
this._animationDone.pipe(
116+
// This needs a `distinctUntilChanged` in order to avoid emitting the same event twice due
117+
// to a bug in animations where the `.done` callback gets invoked twice on some browsers.
118+
// See https://github.com/angular/angular/issues/24084
119+
distinctUntilChanged((x, y) => x.fromState === y.fromState && x.toState === y.toState),
120+
takeUntil(this._destroyed)
121+
).subscribe(event => {
122+
if ((event.toState as StepContentPositionState) === 'current') {
123+
this.animationDone.emit();
124+
}
125+
});
117126
}
118127
}
119128

0 commit comments

Comments
 (0)