@@ -28,9 +28,9 @@ import {
2828import { AnimationEvent } from '@angular/animations' ;
2929import { TemplatePortal , CdkPortalOutlet , PortalHostDirective } from '@angular/cdk/portal' ;
3030import { Directionality , Direction } from '@angular/cdk/bidi' ;
31- import { Subscription } from 'rxjs' ;
31+ import { Subscription , Subject } from 'rxjs' ;
3232import { matTabsAnimations } from './tabs-animations' ;
33- import { startWith } from 'rxjs/operators' ;
33+ import { startWith , distinctUntilChanged } from 'rxjs/operators' ;
3434
3535/**
3636 * These position states are used internally as animation states for the tab body. Setting the
@@ -125,6 +125,9 @@ export class MatTabBody implements OnInit, OnDestroy {
125125 /** Tab body position state. Used by the animation trigger for the current state. */
126126 _position : MatTabBodyPositionState ;
127127
128+ /** Emits when an animation on the tab is complete. */
129+ _translateTabComplete = new Subject < AnimationEvent > ( ) ;
130+
128131 /** Event emitted when the tab begins to animate towards the center as the active tab. */
129132 @Output ( ) readonly _onCentering : EventEmitter < number > = new EventEmitter < number > ( ) ;
130133
@@ -166,6 +169,21 @@ export class MatTabBody implements OnInit, OnDestroy {
166169 changeDetectorRef . markForCheck ( ) ;
167170 } ) ;
168171 }
172+
173+ // Ensure that we get unique animation events, because the `.done` callback can get
174+ // invoked twice in some browsers. See https://github.com/angular/angular/issues/24084.
175+ this . _translateTabComplete . pipe ( distinctUntilChanged ( ( x , y ) => {
176+ return x . fromState === y . fromState && x . toState === y . toState ;
177+ } ) ) . subscribe ( event => {
178+ // If the transition to the center is complete, emit an event.
179+ if ( this . _isCenterPosition ( event . toState ) && this . _isCenterPosition ( this . _position ) ) {
180+ this . _onCentered . emit ( ) ;
181+ }
182+
183+ if ( this . _isCenterPosition ( event . fromState ) && ! this . _isCenterPosition ( this . _position ) ) {
184+ this . _afterLeavingCenter . emit ( ) ;
185+ }
186+ } ) ;
169187 }
170188
171189 /**
@@ -180,27 +198,17 @@ export class MatTabBody implements OnInit, OnDestroy {
180198
181199 ngOnDestroy ( ) {
182200 this . _dirChangeSubscription . unsubscribe ( ) ;
201+ this . _translateTabComplete . complete ( ) ;
183202 }
184203
185- _onTranslateTabStarted ( e : AnimationEvent ) : void {
186- const isCentering = this . _isCenterPosition ( e . toState ) ;
204+ _onTranslateTabStarted ( event : AnimationEvent ) : void {
205+ const isCentering = this . _isCenterPosition ( event . toState ) ;
187206 this . _beforeCentering . emit ( isCentering ) ;
188207 if ( isCentering ) {
189208 this . _onCentering . emit ( this . _elementRef . nativeElement . clientHeight ) ;
190209 }
191210 }
192211
193- _onTranslateTabComplete ( e : AnimationEvent ) : void {
194- // If the transition to the center is complete, emit an event.
195- if ( this . _isCenterPosition ( e . toState ) && this . _isCenterPosition ( this . _position ) ) {
196- this . _onCentered . emit ( ) ;
197- }
198-
199- if ( this . _isCenterPosition ( e . fromState ) && ! this . _isCenterPosition ( this . _position ) ) {
200- this . _afterLeavingCenter . emit ( ) ;
201- }
202- }
203-
204212 /** The text direction of the containing app. */
205213 _getLayoutDirection ( ) : Direction {
206214 return this . _dir && this . _dir . value === 'rtl' ? 'rtl' : 'ltr' ;
0 commit comments