diff --git a/src/lib/expansion/expansion-panel.html b/src/lib/expansion/expansion-panel.html index 745d492a0205..81b6153001fe 100644 --- a/src/lib/expansion/expansion-panel.html +++ b/src/lib/expansion/expansion-panel.html @@ -2,7 +2,7 @@
diff --git a/src/lib/expansion/expansion-panel.ts b/src/lib/expansion/expansion-panel.ts index 98bc0f508064..8c860d37602f 100644 --- a/src/lib/expansion/expansion-panel.ts +++ b/src/lib/expansion/expansion-panel.ts @@ -35,7 +35,7 @@ import { import {DOCUMENT} from '@angular/common'; import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations'; import {Subject} from 'rxjs'; -import {filter, startWith, take} from 'rxjs/operators'; +import {filter, startWith, take, distinctUntilChanged} from 'rxjs/operators'; import {matExpansionAnimations} from './expansion-animations'; import {MatExpansionPanelContent} from './expansion-panel-content'; import {MAT_ACCORDION, MatAccordionBase} from './accordion-base'; @@ -119,6 +119,9 @@ export class MatExpansionPanel extends CdkAccordionItem implements AfterContentI /** ID for the associated header element. Used for a11y labelling. */ _headerId = `mat-expansion-panel-header-${uniqueId++}`; + /** Stream of body animation done events. */ + _bodyAnimationDone = new Subject(); + constructor(@Optional() @SkipSelf() @Inject(MAT_ACCORDION) accordion: MatAccordionBase, _changeDetectorRef: ChangeDetectorRef, _uniqueSelectionDispatcher: UniqueSelectionDispatcher, @@ -129,6 +132,20 @@ export class MatExpansionPanel extends CdkAccordionItem implements AfterContentI super(accordion, _changeDetectorRef, _uniqueSelectionDispatcher); this.accordion = accordion; this._document = _document; + + // We need a Subject with distinctUntilChanged, because the `done` event + // fires twice on some browsers. See https://github.com/angular/angular/issues/24084 + this._bodyAnimationDone.pipe(distinctUntilChanged((x, y) => { + return x.fromState === y.fromState && x.toState === y.toState; + })).subscribe(event => { + if (event.fromState !== 'void') { + if (event.toState === 'expanded') { + this.afterExpand.emit(); + } else if (event.toState === 'collapsed') { + this.afterCollapse.emit(); + } + } + }); } /** Determines whether the expansion panel should have spacing between it and its siblings. */ @@ -166,20 +183,10 @@ export class MatExpansionPanel extends CdkAccordionItem implements AfterContentI ngOnDestroy() { super.ngOnDestroy(); + this._bodyAnimationDone.complete(); this._inputChanges.complete(); } - _bodyAnimation(event: AnimationEvent) { - const {phaseName, toState, fromState} = event; - - if (phaseName === 'done' && toState === 'expanded' && fromState !== 'void') { - this.afterExpand.emit(); - } - if (phaseName === 'done' && toState === 'collapsed' && fromState !== 'void') { - this.afterCollapse.emit(); - } - } - /** Checks whether the expansion panel's content contains the currently-focused element. */ _containsFocus(): boolean { if (this._body && this._document) {