I found solution. You can use RouteReuseStrategy store method to implement custom attach/detach component lifecycle hooks.
If ActivatedRouteSnapshot attached, store method receive null as detachedRouteHandle (null = attached, value = detached).
I use components as route keys and service to store DetachedRouteHandles.
RouteReuseStrategy code:
export class SelectiveRouteReuseStrategy implements RouteReuseStrategy {
constructor(
private detachedRouteHandlesService: DetachedRouteHandlesService,
) { }
public shouldDetach(route: ActivatedRouteSnapshot): boolean {
return !!route.data.reuse;
}
public store(route: ActivatedRouteSnapshot, detachedRouteHandle: DetachedRouteHandle): void {
detachedRouteHandle
? this.detachedRouteHandlesService.set(route.component, detachedRouteHandle)
: this.detachedRouteHandlesService.delete(route.component);
}
public shouldAttach(route: ActivatedRouteSnapshot): boolean {
return !!route.data.reuse ? this.detachedRouteHandlesService.has(route.component) : false;
}
public retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null {
return !!route.data.reuse ? this.detachedRouteHandlesService.get(route.component) : null;
}
public shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
return future.routeConfig === curr.routeConfig;
}
}
DetachedRouteHandlesService has store property - Map with components as keys and DetachedRouteHandle as values; and changes property - Observable to emit store on store change.
DetachedRouteHandlesService code:
export class DetachedRouteHandlesService {
private readonly store: Map<any, DetachedRouteHandle>;
private readonly changes: Subject<Map<any, DetachedRouteHandle>>;
public readonly changes$: Observable<Map<any, DetachedRouteHandle>>;
constructor() {
this.store = new Map();
this.changes = new Subject();
this.changes$ = this.changes.asObservable();
}
private next(): void {
this.changes.next(this.store);
}
public set(component: any, handle: DetachedRouteHandle): void {
this.store.set(component, handle);
this.next();
}
public delete(component: any): void {
this.store.delete(component);
this.next();
}
public get(component: any): DetachedRouteHandle | null {
return this.store.get(component);
}
public has(component: any): boolean {
return this.store.has(component);
}
public clear(): void {
this.store.clear();
this.next();
}
}
In component, simply subscribe on changes and check DetachedRouteHandle. Has handle = detached, else = attached.
Component code:
ngOnInit() {
this.detachedRouteHandlesService.changes$
.pipe(
observeOn(asapScheduler)
).subscribe(changes => {
changes.has(this.activatedRoute.component)
? this.onDetach()
: this.onAttach()
});
}