From 1df79e9efa33cf3e279717677f37caaeeecb5be8 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Tue, 15 Aug 2017 23:48:16 +0200 Subject: [PATCH] perf(tabs): avoid extra resize handler and reflow (#6434) In #6304, an extra window resize handler was added in order to call `_checkPaginationEnabled`. This handler is unnecessary, because we have another resize handler a little bit below that calls `_updatePagination`, which will call `_checkPaginationEnabled` internally. The original issue that #6304 was fixing was due to the fact that the current listener wasn't being run in the Angular zone. These changes remove the extra listener, increase the debounce interval and move the handler outside back into the zone. There may be a slight overhead from moving the listener into the zone, however it should be offset by not having another listener and not calling `_checkPaginationEnabled` twice. --- src/lib/tabs/tab-header.spec.ts | 2 +- src/lib/tabs/tab-header.ts | 57 +++++++++++---------------------- 2 files changed, 20 insertions(+), 39 deletions(-) diff --git a/src/lib/tabs/tab-header.spec.ts b/src/lib/tabs/tab-header.spec.ts index 50a37a59ff1..46ba2a4b90d 100644 --- a/src/lib/tabs/tab-header.spec.ts +++ b/src/lib/tabs/tab-header.spec.ts @@ -259,7 +259,7 @@ describe('MdTabHeader', () => { spyOn(inkBar, 'alignToElement'); dispatchFakeEvent(window, 'resize'); - tick(10); + tick(150); fixture.detectChanges(); expect(inkBar.alignToElement).toHaveBeenCalled(); diff --git a/src/lib/tabs/tab-header.ts b/src/lib/tabs/tab-header.ts index 64106c46c48..f21ea0d92df 100644 --- a/src/lib/tabs/tab-header.ts +++ b/src/lib/tabs/tab-header.ts @@ -20,10 +20,9 @@ import { AfterContentChecked, AfterContentInit, OnDestroy, - NgZone, Renderer2, ChangeDetectionStrategy, - ChangeDetectorRef + ChangeDetectorRef, } from '@angular/core'; import {Directionality, Direction} from '@angular/cdk/bidi'; import {RIGHT_ARROW, LEFT_ARROW, ENTER} from '@angular/cdk/keycodes'; @@ -35,8 +34,6 @@ import {fromEvent} from 'rxjs/observable/fromEvent'; import {MdTabLabelWrapper} from './tab-label-wrapper'; import {MdInkBar} from './ink-bar'; import {CanDisableRipple, mixinDisableRipple} from '../core/common-behaviors/disable-ripple'; -import {RxChain, debounceTime} from '@angular/cdk/rxjs'; -import {Platform} from '@angular/cdk/platform'; /** * The directions that scrolling can go in when the header's tabs exceed the header width. 'After' @@ -117,15 +114,11 @@ export class MdTabHeader extends _MdTabHeaderMixinBase private _selectedIndex: number = 0; - /** subscription for the window resize handler */ - private _resizeSubscription: Subscription | null; - /** The index of the active tab. */ @Input() get selectedIndex(): number { return this._selectedIndex; } set selectedIndex(value: number) { this._selectedIndexChanged = this._selectedIndex != value; - this._selectedIndex = value; this._focusIndex = value; } @@ -137,19 +130,10 @@ export class MdTabHeader extends _MdTabHeaderMixinBase @Output() indexFocused = new EventEmitter(); constructor(private _elementRef: ElementRef, - private _ngZone: NgZone, private _renderer: Renderer2, private _changeDetectorRef: ChangeDetectorRef, - @Optional() private _dir: Directionality, - platform: Platform) { + @Optional() private _dir: Directionality) { super(); - - if (platform.isBrowser) { - // TODO: Add library level window listener https://goo.gl/y25X5M - this._resizeSubscription = RxChain.from(fromEvent(window, 'resize')) - .call(debounceTime, 150) - .subscribe(() => this._checkPaginationEnabled()); - } } ngAfterContentChecked(): void { @@ -197,16 +181,14 @@ export class MdTabHeader extends _MdTabHeaderMixinBase * Aligns the ink bar to the selected tab on load. */ ngAfterContentInit() { - this._realignInkBar = this._ngZone.runOutsideAngular(() => { - let dirChange = this._dir ? this._dir.change : observableOf(null); - let resize = typeof window !== 'undefined' ? - auditTime.call(fromEvent(window, 'resize'), 10) : - observableOf(null); + const dirChange = this._dir ? this._dir.change : observableOf(null); + const resize = typeof window !== 'undefined' ? + auditTime.call(fromEvent(window, 'resize'), 150) : + observableOf(null); - return startWith.call(merge(dirChange, resize), null).subscribe(() => { - this._updatePagination(); - this._alignInkBarToSelectedTab(); - }); + this._realignInkBar = startWith.call(merge(dirChange, resize), null).subscribe(() => { + this._updatePagination(); + this._alignInkBarToSelectedTab(); }); } @@ -215,11 +197,6 @@ export class MdTabHeader extends _MdTabHeaderMixinBase this._realignInkBar.unsubscribe(); this._realignInkBar = null; } - - if (this._resizeSubscription) { - this._resizeSubscription.unsubscribe(); - this._resizeSubscription = null; - } } /** @@ -400,14 +377,18 @@ export class MdTabHeader extends _MdTabHeaderMixinBase * should be called sparingly. */ _checkPaginationEnabled() { - this._showPaginationControls = + const isEnabled = this._tabList.nativeElement.scrollWidth > this._elementRef.nativeElement.offsetWidth; - if (!this._showPaginationControls) { + if (!isEnabled) { this.scrollDistance = 0; } - this._changeDetectorRef.markForCheck(); + if (isEnabled !== this._showPaginationControls) { + this._changeDetectorRef.markForCheck(); + } + + this._showPaginationControls = isEnabled; } /** @@ -441,9 +422,9 @@ export class MdTabHeader extends _MdTabHeaderMixinBase /** Tells the ink-bar to align itself to the current label wrapper */ private _alignInkBarToSelectedTab(): void { - const selectedLabelWrapper = this._labelWrappers && this._labelWrappers.length - ? this._labelWrappers.toArray()[this.selectedIndex].elementRef.nativeElement - : null; + const selectedLabelWrapper = this._labelWrappers && this._labelWrappers.length ? + this._labelWrappers.toArray()[this.selectedIndex].elementRef.nativeElement : + null; this._inkBar.alignToElement(selectedLabelWrapper); }