Skip to content

Commit

Permalink
Implement clearing tippy instances from storage on directive destroy …
Browse files Browse the repository at this point in the history
…to fix memory leak
  • Loading branch information
farengeyt451 committed Jul 1, 2021
1 parent f3d683a commit b7d581c
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 20 deletions.
5 changes: 3 additions & 2 deletions projects/demo/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 8 additions & 7 deletions projects/lib-workboard/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { isPlatformServer } from '@angular/common';
import { AfterViewInit, Component, ElementRef, Inject, Input, PLATFORM_ID, ViewChild } from '@angular/core';
import { AfterViewInit, Component, ElementRef, Inject, Input, OnDestroy, PLATFORM_ID, ViewChild } from '@angular/core';
import { createSingleton } from 'tippy.js';
import {
NgxSingletonProps,
Expand All @@ -20,18 +20,25 @@ import { NgxTippyService } from './ngx-tippy.service';
</div>
`,
})
export class NgxTippySingletonComponent implements AfterViewInit {
export class NgxTippySingletonComponent implements AfterViewInit, OnDestroy {
@Input() singletonProps?: NgxSingletonProps;
@Input() singletonName?: string;
@ViewChild('contentWrapper', { read: ElementRef, static: false }) contentWrapper: ElementRef;

private singletonInstance!: NgxTippySingletonInstance;
private currentSingletonChildrenTippyInstances!: NgxTippyInstance[] | undefined;

constructor(@Inject(PLATFORM_ID) private platform: Object, private ngxTippyService: NgxTippyService) {}

ngAfterViewInit() {
if (isPlatformServer(this.platform)) return;
this.setSingleton();
}

ngOnDestroy() {
this.clearSingletonInstance();
}

/**
* Take projected in component tooltips element
* Take initiated tippy instances
Expand All @@ -46,20 +53,20 @@ export class NgxTippySingletonComponent implements AfterViewInit {
const tippyInstances: Map<string, NgxTippyInstance> | null = this.ngxTippyService.getInstances();
const tippyInstancesSerialized: NgxTippyInstance[] | undefined = tippyInstances && [...tippyInstances.values()];

const currentSingletonChildrenTippyInstances: NgxTippyInstance[] | undefined =
this.currentSingletonChildrenTippyInstances =
tippyInstancesSerialized &&
tippyInstancesSerialized.filter((tippyInstance) => singletonTooltipIDs.includes(tippyInstance.id));

if (currentSingletonChildrenTippyInstances?.length) {
this.initTippySingleton(currentSingletonChildrenTippyInstances);
if (this.currentSingletonChildrenTippyInstances?.length) {
this.initTippySingleton(this.currentSingletonChildrenTippyInstances);
} else {
throw new Error(`No children tippy instances founded within singleton component`);
}
}

private initTippySingleton(childrenSingletonInstances: NgxTippyInstance[]) {
const singleton: NgxTippySingletonInstance = createSingleton(childrenSingletonInstances, this.singletonProps);
this.writeSingletonInstanceToStorage(singleton);
this.singletonInstance = createSingleton(childrenSingletonInstances, this.singletonProps);
this.writeSingletonInstanceToStorage(this.singletonInstance);
}

/**
Expand All @@ -73,7 +80,7 @@ export class NgxTippySingletonComponent implements AfterViewInit {
const extendedSingletonInstance = this.extendShowFn(singletonInstance);

this.ngxTippyService.setSingletonInstance(
this.singletonName || `singleton-${singletonInstance.id}}`,
this.singletonName || `singleton-${singletonInstance.id}`,
extendedSingletonInstance
);
}
Expand All @@ -98,4 +105,21 @@ export class NgxTippySingletonComponent implements AfterViewInit {

return singletonInstance;
}

private clearSingletonInstance() {
const singletonInstances = this.ngxTippyService.getSingletonInstances();

if (singletonInstances && this.singletonInstance) {
this.destroySingletonInstance();
this.deleteEntryInStorage(singletonInstances);
}
}

private destroySingletonInstance() {
this.singletonInstance.destroy();
}

private deleteEntryInStorage(singletonInstances: Map<string, NgxTippySingletonInstance>) {
singletonInstances.delete(this.singletonName || `singleton-${this.singletonInstance.id}`);
}
}
21 changes: 18 additions & 3 deletions projects/ngx-tippy-wrapper/src/lib/ngx-tippy.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export class NgxTippyDirective implements OnInit, OnDestroy {
}

ngOnDestroy() {
this.deleteEntryInStorage();
this.clearInstance();
}

/**
Expand Down Expand Up @@ -75,7 +75,22 @@ export class NgxTippyDirective implements OnInit, OnDestroy {
this.ngxTippyService.setInstance(this.tippyName || `tippy-${tippyInstance.id}`, tippyInstance);
}

private deleteEntryInStorage() {
this.ngxTippyService.getInstances()?.delete(this.tippyName || `tippy-${this.tippyInstance.id}`);
private clearInstance() {
const instances = this.ngxTippyService.getInstances();

if (instances && this.tippyInstance) {
const tippyName = this.tippyName || `tippy-${this.tippyInstance.id}`;

this.destroyTippyInstance(tippyName);
this.deleteEntryInStorage(instances, tippyName);
}
}

private destroyTippyInstance(tippyName: string) {
this.ngxTippyService.destroy(tippyName);
}

private deleteEntryInStorage(instances: Map<string, NgxTippyInstance>, tippyName: string) {
instances.delete(tippyName);
}
}

0 comments on commit b7d581c

Please sign in to comment.