delete stored route when attached

This commit is contained in:
Dan Percic 2023-03-26 14:49:32 +03:00
parent e1f818c109
commit 4f58a6603e
2 changed files with 42 additions and 29 deletions

View File

@ -1,5 +1,7 @@
import { ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy } from '@angular/router'; import { ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy } from '@angular/router';
import { Debounce } from '../utils'; import { Debounce } from '../utils';
import { ComponentRef, Injectable } from '@angular/core';
import { Subject } from 'rxjs';
export interface OnAttach { export interface OnAttach {
ngOnAttach(previousRoute?: ActivatedRouteSnapshot): void; ngOnAttach(previousRoute?: ActivatedRouteSnapshot): void;
@ -9,13 +11,23 @@ export interface OnDetach {
ngOnDetach(): void; ngOnDetach(): void;
} }
interface RouteStorageObject { export function getReusableRouteKey(route: ActivatedRouteSnapshot): string {
handle: DetachedRouteHandle; return route.pathFromRoot
previousRoute: ActivatedRouteSnapshot; .map((el: ActivatedRouteSnapshot) => (el.routeConfig ? `${el.routeConfig.path}${JSON.stringify(el.params)}` : ''))
.filter(str => str.length > 0)
.join('');
} }
interface RouteStorageObject {
readonly handle: DetachedRouteHandle;
readonly previousRoute: ActivatedRouteSnapshot;
}
@Injectable({ providedIn: 'root' })
export class CustomRouteReuseStrategy implements RouteReuseStrategy { export class CustomRouteReuseStrategy implements RouteReuseStrategy {
private _storedRoutes: { [key: string]: RouteStorageObject } = {}; readonly attached$ = new Subject<ActivatedRouteSnapshot>();
readonly detached$ = new Subject<ActivatedRouteSnapshot>();
readonly #storedRoutes = new Map<string, RouteStorageObject>();
private static _removeTooltips(): void { private static _removeTooltips(): void {
while (document.getElementsByTagName('mat-tooltip-component').length > 0) { while (document.getElementsByTagName('mat-tooltip-component').length > 0) {
@ -27,7 +39,7 @@ export class CustomRouteReuseStrategy implements RouteReuseStrategy {
if (!route?.routeConfig?.data) { if (!route?.routeConfig?.data) {
return false; return false;
} }
return !!route.routeConfig.data['reuse'] && !!this._getKey(route); return !!route.routeConfig.data['reuse'] && !!getReusableRouteKey(route);
} }
store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void { store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
@ -36,44 +48,45 @@ export class CustomRouteReuseStrategy implements RouteReuseStrategy {
} }
CustomRouteReuseStrategy._removeTooltips(); CustomRouteReuseStrategy._removeTooltips();
// eslint-disable-next-line @typescript-eslint/no-explicit-any const element = <{ componentRef: ComponentRef<OnDetach> } | undefined>handle;
const element: any = handle;
if (element?.componentRef?.instance?.ngOnDetach) { if (element?.componentRef?.instance?.ngOnDetach) {
this._onDetach(element.componentRef?.instance); this._onDetach(element.componentRef.instance);
} }
this._storedRoutes[this._getKey(route)] = { if (element) {
this.detached$.next(route);
}
this.#storedRoutes.set(getReusableRouteKey(route), {
handle: element as DetachedRouteHandle, handle: element as DetachedRouteHandle,
previousRoute: route, previousRoute: route,
}; });
} }
shouldAttach(route: ActivatedRouteSnapshot): boolean { shouldAttach(route: ActivatedRouteSnapshot): boolean {
return !!this._storedRoutes[this._getKey(route)]; return this.#storedRoutes.has(getReusableRouteKey(route));
} }
retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle { retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
const key = this._getKey(route); const key = getReusableRouteKey(route);
// eslint-disable-next-line @typescript-eslint/no-explicit-any const value = this.#storedRoutes.get(key);
const element: any = this._storedRoutes[key]?.handle; const element = <{ componentRef: ComponentRef<OnAttach> } | undefined>value?.handle;
if (element?.componentRef?.instance?.ngOnAttach) { if (element?.componentRef?.instance.ngOnAttach && value) {
this._onAttach(element.componentRef?.instance, this._storedRoutes[key].previousRoute); this._onAttach(element.componentRef.instance, value.previousRoute);
setTimeout(() => this.#storedRoutes.delete(key));
}
if (element) {
this.attached$.next(route);
} }
return element as DetachedRouteHandle; return element as DetachedRouteHandle;
} }
shouldReuseRoute(future: ActivatedRouteSnapshot, current: ActivatedRouteSnapshot): boolean { shouldReuseRoute(future: ActivatedRouteSnapshot, current: ActivatedRouteSnapshot): boolean {
return this._getKey(future) === this._getKey(current); return getReusableRouteKey(future) === getReusableRouteKey(current);
}
private _getKey(route: ActivatedRouteSnapshot): string {
return route.pathFromRoot
.map((el: ActivatedRouteSnapshot) => (el.routeConfig ? `${el.routeConfig.path}${JSON.stringify(el.params)}` : ''))
.filter(str => str.length > 0)
.join('');
} }
@Debounce() @Debounce()

View File

@ -1,4 +1,5 @@
{ {
"compileOnSave": false,
"compilerOptions": { "compilerOptions": {
"outDir": "../../dist/out-tsc", "outDir": "../../dist/out-tsc",
"strict": true, "strict": true,
@ -12,16 +13,15 @@
"sourceMap": true, "sourceMap": true,
"downlevelIteration": true, "downlevelIteration": true,
"experimentalDecorators": true, "experimentalDecorators": true,
"noImplicitAny": true,
"moduleResolution": "node", "moduleResolution": "node",
"importHelpers": true, "importHelpers": true,
"resolveJsonModule": true, "resolveJsonModule": true,
"target": "es2021", "target": "ES2022",
"module": "es2020", "module": "ES2022",
"lib": ["es2021", "dom"], "lib": ["ES2022", "dom"],
"allowSyntheticDefaultImports": true "allowSyntheticDefaultImports": true
}, },
"include": ["src/**/*.ts"], "include": ["./**/*"],
"angularCompilerOptions": { "angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false, "enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true, "strictInjectionParameters": true,