Skip to content

Commit 85a3ae0

Browse files
crisbetojosephperrott
authored andcommitted
fix(expansion-panel): emitting events twice on some browsers (#13600)
1 parent c271167 commit 85a3ae0

File tree

2 files changed

+20
-13
lines changed

2 files changed

+20
-13
lines changed

src/lib/expansion/expansion-panel.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<div class="mat-expansion-panel-content"
33
role="region"
44
[@bodyExpansion]="_getExpandedState()"
5-
(@bodyExpansion.done)="_bodyAnimation($event)"
5+
(@bodyExpansion.done)="_bodyAnimationDone.next($event)"
66
[attr.aria-labelledby]="_headerId"
77
[id]="id"
88
#body>

src/lib/expansion/expansion-panel.ts

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import {
3535
import {DOCUMENT} from '@angular/common';
3636
import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations';
3737
import {Subject} from 'rxjs';
38-
import {filter, startWith, take} from 'rxjs/operators';
38+
import {filter, startWith, take, distinctUntilChanged} from 'rxjs/operators';
3939
import {matExpansionAnimations} from './expansion-animations';
4040
import {MatExpansionPanelContent} from './expansion-panel-content';
4141
import {MAT_ACCORDION, MatAccordionBase} from './accordion-base';
@@ -119,6 +119,9 @@ export class MatExpansionPanel extends CdkAccordionItem implements AfterContentI
119119
/** ID for the associated header element. Used for a11y labelling. */
120120
_headerId = `mat-expansion-panel-header-${uniqueId++}`;
121121

122+
/** Stream of body animation done events. */
123+
_bodyAnimationDone = new Subject<AnimationEvent>();
124+
122125
constructor(@Optional() @SkipSelf() @Inject(MAT_ACCORDION) accordion: MatAccordionBase,
123126
_changeDetectorRef: ChangeDetectorRef,
124127
_uniqueSelectionDispatcher: UniqueSelectionDispatcher,
@@ -129,6 +132,20 @@ export class MatExpansionPanel extends CdkAccordionItem implements AfterContentI
129132
super(accordion, _changeDetectorRef, _uniqueSelectionDispatcher);
130133
this.accordion = accordion;
131134
this._document = _document;
135+
136+
// We need a Subject with distinctUntilChanged, because the `done` event
137+
// fires twice on some browsers. See https://github.com/angular/angular/issues/24084
138+
this._bodyAnimationDone.pipe(distinctUntilChanged((x, y) => {
139+
return x.fromState === y.fromState && x.toState === y.toState;
140+
})).subscribe(event => {
141+
if (event.fromState !== 'void') {
142+
if (event.toState === 'expanded') {
143+
this.afterExpand.emit();
144+
} else if (event.toState === 'collapsed') {
145+
this.afterCollapse.emit();
146+
}
147+
}
148+
});
132149
}
133150

134151
/** Determines whether the expansion panel should have spacing between it and its siblings. */
@@ -166,20 +183,10 @@ export class MatExpansionPanel extends CdkAccordionItem implements AfterContentI
166183

167184
ngOnDestroy() {
168185
super.ngOnDestroy();
186+
this._bodyAnimationDone.complete();
169187
this._inputChanges.complete();
170188
}
171189

172-
_bodyAnimation(event: AnimationEvent) {
173-
const {phaseName, toState, fromState} = event;
174-
175-
if (phaseName === 'done' && toState === 'expanded' && fromState !== 'void') {
176-
this.afterExpand.emit();
177-
}
178-
if (phaseName === 'done' && toState === 'collapsed' && fromState !== 'void') {
179-
this.afterCollapse.emit();
180-
}
181-
}
182-
183190
/** Checks whether the expansion panel's content contains the currently-focused element. */
184191
_containsFocus(): boolean {
185192
if (this._body && this._document) {

0 commit comments

Comments
 (0)