From beb473949b197de914b6233c748dbcd7c7baf2f1 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Fri, 19 Oct 2018 17:18:37 +0200 Subject: [PATCH] fix(sidenav): opened and closed events emitting twice on IE and Edge (#13649) --- src/lib/sidenav/drawer.ts | 48 ++++++++++++++++++++++++-------------- src/lib/sidenav/sidenav.ts | 4 ++-- 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/src/lib/sidenav/drawer.ts b/src/lib/sidenav/drawer.ts index ebb6a7907df..33744175f88 100644 --- a/src/lib/sidenav/drawer.ts +++ b/src/lib/sidenav/drawer.ts @@ -37,7 +37,15 @@ import { ViewEncapsulation, } from '@angular/core'; import {fromEvent, merge, Observable, Subject} from 'rxjs'; -import {debounceTime, filter, map, startWith, take, takeUntil} from 'rxjs/operators'; +import { + debounceTime, + filter, + map, + startWith, + take, + takeUntil, + distinctUntilChanged, +} from 'rxjs/operators'; import {matDrawerAnimations} from './drawer-animations'; import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations'; @@ -108,8 +116,8 @@ export class MatDrawerContent extends CdkScrollable implements AfterContentInit host: { 'class': 'mat-drawer', '[@transform]': '_animationState', - '(@transform.start)': '_onAnimationStart($event)', - '(@transform.done)': '_onAnimationEnd($event)', + '(@transform.start)': '_animationStarted.next($event)', + '(@transform.done)': '_animationEnd.next($event)', // must prevent the browser from aligning text based on value '[attr.align]': 'null', '[class.mat-drawer-end]': 'position === "end"', @@ -166,7 +174,10 @@ export class MatDrawer implements AfterContentInit, AfterContentChecked, OnDestr private _openedVia: FocusOrigin | null; /** Emits whenever the drawer has started animating. */ - _animationStarted = new EventEmitter(); + _animationStarted = new Subject(); + + /** Emits whenever the drawer is done animating. */ + _animationEnd = new Subject(); /** Current state of the sidenav animation. */ _animationState: 'open-instant' | 'open' | 'void' = 'void'; @@ -255,6 +266,19 @@ export class MatDrawer implements AfterContentInit, AfterContentChecked, OnDestr event.stopPropagation(); })); }); + + // We need a Subject with distinctUntilChanged, because the `done` event + // fires twice on some browsers. See https://github.com/angular/angular/issues/24084 + this._animationEnd.pipe(distinctUntilChanged((x, y) => { + return x.fromState === y.fromState && x.toState === y.toState; + })).subscribe((event: AnimationEvent) => { + const {fromState, toState} = event; + + if ((toState.indexOf('open') === 0 && fromState === 'void') || + (toState === 'void' && fromState.indexOf('open') === 0)) { + this.openedChange.emit(this._opened); + } + }); } /** Traps focus inside the drawer. */ @@ -314,6 +338,9 @@ export class MatDrawer implements AfterContentInit, AfterContentChecked, OnDestr if (this._focusTrap) { this._focusTrap.destroy(); } + + this._animationStarted.complete(); + this._animationEnd.complete(); } /** @@ -367,19 +394,6 @@ export class MatDrawer implements AfterContentInit, AfterContentChecked, OnDestr }); } - _onAnimationStart(event: AnimationEvent) { - this._animationStarted.emit(event); - } - - _onAnimationEnd(event: AnimationEvent) { - const {fromState, toState} = event; - - if ((toState.indexOf('open') === 0 && fromState === 'void') || - (toState === 'void' && fromState.indexOf('open') === 0)) { - this.openedChange.emit(this._opened); - } - } - get _width(): number { return this._elementRef.nativeElement ? (this._elementRef.nativeElement.offsetWidth || 0) : 0; } diff --git a/src/lib/sidenav/sidenav.ts b/src/lib/sidenav/sidenav.ts index 28f47becbb5..effd1fe5edb 100644 --- a/src/lib/sidenav/sidenav.ts +++ b/src/lib/sidenav/sidenav.ts @@ -60,8 +60,8 @@ export class MatSidenavContent extends MatDrawerContent { 'class': 'mat-drawer mat-sidenav', 'tabIndex': '-1', '[@transform]': '_animationState', - '(@transform.start)': '_onAnimationStart($event)', - '(@transform.done)': '_onAnimationEnd($event)', + '(@transform.start)': '_animationStarted.next($event)', + '(@transform.done)': '_animationEnd.next($event)', // must prevent the browser from aligning text based on value '[attr.align]': 'null', '[class.mat-drawer-end]': 'position === "end"',