common-ui/src/lib/utils/custom-route-reuse.strategy.ts
2024-06-19 13:05:12 +03:00

102 lines
3.3 KiB
TypeScript

import { ComponentRef, Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy } from '@angular/router';
import { Subject } from 'rxjs';
import { Debounce } from './decorators/debounce.decorator';
export interface OnAttach {
ngOnAttach(previousRoute?: ActivatedRouteSnapshot): void;
}
export interface OnDetach {
ngOnDetach(): void;
}
export function getReusableRouteKey(route: ActivatedRouteSnapshot): string {
return route.pathFromRoot
.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 {
readonly #storedRoutes = new Map<string, RouteStorageObject>();
readonly attached$ = new Subject<ActivatedRouteSnapshot>();
readonly detached$ = new Subject<ActivatedRouteSnapshot>();
private static _removeTooltips(): void {
while (document.getElementsByTagName('mat-tooltip-component').length > 0) {
document.getElementsByTagName('mat-tooltip-component')[0].remove();
}
}
shouldDetach(route: ActivatedRouteSnapshot): boolean {
if (!route?.routeConfig?.data) {
return false;
}
return !!route.routeConfig.data['reuse'] && !!getReusableRouteKey(route);
}
store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
if (handle === null) {
return;
}
CustomRouteReuseStrategy._removeTooltips();
const element = <{ componentRef: ComponentRef<OnDetach> } | undefined>handle;
if (element?.componentRef?.instance?.ngOnDetach) {
this._onDetach(element.componentRef.instance);
}
if (element) {
this.detached$.next(route);
}
this.#storedRoutes.set(getReusableRouteKey(route), {
handle: element as DetachedRouteHandle,
previousRoute: route,
});
}
shouldAttach(route: ActivatedRouteSnapshot): boolean {
return this.#storedRoutes.has(getReusableRouteKey(route));
}
retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
const key = getReusableRouteKey(route);
const value = this.#storedRoutes.get(key);
const element = <{ componentRef: ComponentRef<OnAttach> } | undefined>value?.handle;
if (element?.componentRef?.instance.ngOnAttach && value) {
this._onAttach(element.componentRef.instance, value.previousRoute);
setTimeout(() => this.#storedRoutes.delete(key));
}
if (element) {
this.attached$.next(route);
}
return element as DetachedRouteHandle;
}
shouldReuseRoute(future: ActivatedRouteSnapshot, current: ActivatedRouteSnapshot): boolean {
return getReusableRouteKey(future) === getReusableRouteKey(current);
}
@Debounce()
private _onAttach(instance: OnAttach, previousRoute?: ActivatedRouteSnapshot) {
instance.ngOnAttach(previousRoute);
}
@Debounce()
private _onDetach(instance: OnDetach) {
instance.ngOnDetach();
}
}