Skip to content

Commit 66a01fb

Browse files
Eddmanjelbourn
authored andcommitted
fix(tooltip): memory leak in _setTooltipMessage (#6782)
1 parent 6d4e052 commit 66a01fb

File tree

1 file changed

+22
-5
lines changed

1 file changed

+22
-5
lines changed

src/lib/tooltip/tooltip.ts

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
import {Platform} from '@angular/cdk/platform';
2727
import {ComponentPortal} from '@angular/cdk/portal';
2828
import {take} from 'rxjs/operators/take';
29+
import {takeUntil} from 'rxjs/operators/takeUntil';
2930
import {filter} from 'rxjs/operators/filter';
3031
import {
3132
ChangeDetectionStrategy,
@@ -188,6 +189,9 @@ export class MatTooltip implements OnDestroy {
188189

189190
private _manualListeners = new Map<string, Function>();
190191

192+
/** Emits when the component is destroyed. */
193+
private readonly _destroyed = new Subject<void>();
194+
191195
constructor(
192196
private _overlay: Overlay,
193197
private _elementRef: ElementRef,
@@ -224,7 +228,7 @@ export class MatTooltip implements OnDestroy {
224228
element.style.webkitUserSelect = element.style.userSelect = '';
225229
}
226230

227-
_focusMonitor.monitor(element).subscribe(origin => {
231+
_focusMonitor.monitor(element).pipe(takeUntil(this._destroyed)).subscribe(origin => {
228232
// Note that the focus monitor runs outside the Angular zone.
229233
if (!origin) {
230234
_ngZone.run(() => this.hide(0));
@@ -251,6 +255,9 @@ export class MatTooltip implements OnDestroy {
251255
this._manualListeners.clear();
252256
}
253257

258+
this._destroyed.next();
259+
this._destroyed.complete();
260+
254261
this._ariaDescriber.removeDescription(this._elementRef.nativeElement, this.message);
255262
this._focusMonitor.stopMonitoring(this._elementRef.nativeElement);
256263
}
@@ -264,7 +271,9 @@ export class MatTooltip implements OnDestroy {
264271
this._detach();
265272
this._portal = this._portal || new ComponentPortal(TooltipComponent, this._viewContainerRef);
266273
this._tooltipInstance = overlayRef.attach(this._portal).instance;
267-
this._tooltipInstance.afterHidden().subscribe(() => this._detach());
274+
this._tooltipInstance.afterHidden()
275+
.pipe(takeUntil(this._destroyed))
276+
.subscribe(() => this._detach());
268277
this._setTooltipClass(this._tooltipClass);
269278
this._updateTooltipMessage();
270279
this._tooltipInstance!.show(this._position, delay);
@@ -318,7 +327,10 @@ export class MatTooltip implements OnDestroy {
318327
this._scrollDispatcher.getAncestorScrollContainers(this._elementRef)
319328
);
320329

321-
strategy.onPositionChange.pipe(filter(() => !!this._tooltipInstance)).subscribe(change => {
330+
strategy.onPositionChange.pipe(
331+
filter(() => !!this._tooltipInstance),
332+
takeUntil(this._destroyed)
333+
).subscribe(change => {
322334
if (change.scrollableViewProperties.isOverlayClipped && this._tooltipInstance!.isVisible()) {
323335
// After position changes occur and the overlay is clipped by
324336
// a parent scrollable then close the tooltip.
@@ -336,7 +348,9 @@ export class MatTooltip implements OnDestroy {
336348
scrollStrategy: this._scrollStrategy()
337349
});
338350

339-
this._overlayRef.detachments().subscribe(() => this._detach());
351+
this._overlayRef.detachments()
352+
.pipe(takeUntil(this._destroyed))
353+
.subscribe(() => this._detach());
340354

341355
return this._overlayRef;
342356
}
@@ -429,7 +443,10 @@ export class MatTooltip implements OnDestroy {
429443
this._tooltipInstance.message = this.message;
430444
this._tooltipInstance._markForCheck();
431445

432-
this._ngZone.onMicrotaskEmpty.asObservable().pipe(take(1)).subscribe(() => {
446+
this._ngZone.onMicrotaskEmpty.asObservable().pipe(
447+
take(1),
448+
takeUntil(this._destroyed)
449+
).subscribe(() => {
433450
if (this._tooltipInstance) {
434451
this._overlayRef!.updatePosition();
435452
}

0 commit comments

Comments
 (0)