From 4b62f0af68ab4409da19902e80bb0a2f1655b517 Mon Sep 17 00:00:00 2001 From: crisbeto Date: Sat, 27 Jun 2020 14:06:29 +0200 Subject: [PATCH] fix(overlay): only dispatch position change event if requested The `ConnectedOverlayPositionChange` event can be expensive to calculate since it contains information about the scroll position. We have some logic in the position strategy about not emitting it if there are no observers, but in the `CdkConnectedOverlay` directive we were always subscribing in order to proxy it to the output. These changes add some logic to only subscribe when necessary. --- src/cdk/overlay/overlay-directives.ts | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/cdk/overlay/overlay-directives.ts b/src/cdk/overlay/overlay-directives.ts index f7d3641cf4f6..888fcbdd55e4 100644 --- a/src/cdk/overlay/overlay-directives.ts +++ b/src/cdk/overlay/overlay-directives.ts @@ -26,6 +26,7 @@ import { ViewContainerRef, } from '@angular/core'; import {Subscription} from 'rxjs'; +import {takeWhile} from 'rxjs/operators'; import {Overlay} from './overlay'; import {OverlayConfig} from './overlay-config'; import {OverlayRef} from './overlay-ref'; @@ -113,6 +114,7 @@ export class CdkConnectedOverlay implements OnDestroy, OnChanges { private _backdropSubscription = Subscription.EMPTY; private _attachSubscription = Subscription.EMPTY; private _detachSubscription = Subscription.EMPTY; + private _positionSubscription = Subscription.EMPTY; private _offsetX: number; private _offsetY: number; private _position: FlexibleConnectedPositionStrategy; @@ -254,6 +256,7 @@ export class CdkConnectedOverlay implements OnDestroy, OnChanges { this._attachSubscription.unsubscribe(); this._detachSubscription.unsubscribe(); this._backdropSubscription.unsubscribe(); + this._positionSubscription.unsubscribe(); if (this._overlayRef) { this._overlayRef.dispose(); @@ -367,10 +370,7 @@ export class CdkConnectedOverlay implements OnDestroy, OnChanges { /** Returns the position strategy of the overlay to be set on the overlay config */ private _createPositionStrategy(): FlexibleConnectedPositionStrategy { const strategy = this._overlay.position().flexibleConnectedTo(this.origin.elementRef); - this._updatePositionStrategy(strategy); - strategy.positionChanges.subscribe(p => this.positionChange.emit(p)); - return strategy; } @@ -394,6 +394,22 @@ export class CdkConnectedOverlay implements OnDestroy, OnChanges { } else { this._backdropSubscription.unsubscribe(); } + + this._positionSubscription.unsubscribe(); + + // Only subscribe to `positionChanges` if requested, because putting + // together all the information for it can be expensive. + if (this.positionChange.observers.length > 0) { + this._positionSubscription = this._position.positionChanges + .pipe(takeWhile(() => this.positionChange.observers.length > 0)) + .subscribe(position => { + this.positionChange.emit(position); + + if (this.positionChange.observers.length === 0) { + this._positionSubscription.unsubscribe(); + } + }); + } } /** Detaches the overlay and unsubscribes to backdrop clicks if backdrop exists */ @@ -403,6 +419,7 @@ export class CdkConnectedOverlay implements OnDestroy, OnChanges { } this._backdropSubscription.unsubscribe(); + this._positionSubscription.unsubscribe(); } static ngAcceptInputType_hasBackdrop: BooleanInput;