@@ -35,7 +35,7 @@ import {
35
35
import { DOCUMENT } from '@angular/common' ;
36
36
import { ANIMATION_MODULE_TYPE } from '@angular/platform-browser/animations' ;
37
37
import { Subject } from 'rxjs' ;
38
- import { filter , startWith , take } from 'rxjs/operators' ;
38
+ import { filter , startWith , take , distinctUntilChanged } from 'rxjs/operators' ;
39
39
import { matExpansionAnimations } from './expansion-animations' ;
40
40
import { MatExpansionPanelContent } from './expansion-panel-content' ;
41
41
import { MAT_ACCORDION , MatAccordionBase } from './accordion-base' ;
@@ -119,6 +119,9 @@ export class MatExpansionPanel extends CdkAccordionItem implements AfterContentI
119
119
/** ID for the associated header element. Used for a11y labelling. */
120
120
_headerId = `mat-expansion-panel-header-${ uniqueId ++ } ` ;
121
121
122
+ /** Stream of body animation done events. */
123
+ _bodyAnimationDone = new Subject < AnimationEvent > ( ) ;
124
+
122
125
constructor ( @Optional ( ) @SkipSelf ( ) @Inject ( MAT_ACCORDION ) accordion : MatAccordionBase ,
123
126
_changeDetectorRef : ChangeDetectorRef ,
124
127
_uniqueSelectionDispatcher : UniqueSelectionDispatcher ,
@@ -129,6 +132,20 @@ export class MatExpansionPanel extends CdkAccordionItem implements AfterContentI
129
132
super ( accordion , _changeDetectorRef , _uniqueSelectionDispatcher ) ;
130
133
this . accordion = accordion ;
131
134
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
+ } ) ;
132
149
}
133
150
134
151
/** Determines whether the expansion panel should have spacing between it and its siblings. */
@@ -166,20 +183,10 @@ export class MatExpansionPanel extends CdkAccordionItem implements AfterContentI
166
183
167
184
ngOnDestroy ( ) {
168
185
super . ngOnDestroy ( ) ;
186
+ this . _bodyAnimationDone . complete ( ) ;
169
187
this . _inputChanges . complete ( ) ;
170
188
}
171
189
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
-
183
190
/** Checks whether the expansion panel's content contains the currently-focused element. */
184
191
_containsFocus ( ) : boolean {
185
192
if ( this . _body && this . _document ) {
0 commit comments