Managing subscription with route reuse

Viewed 1272

Say I have a component and subscription happens in onInit and unsubscribing in onDestroy.

ngOnInit(){
  // subscribe
}

ngOnDestroy() {
  // unsubscribe
}

But now am switching to custome route reuse strategy where the component will be reused. My question is how do I manage those subscription so that onInit onDestroy won't be called when I navigate back to the component's route.

As in how do I unsubscribe when user navigate away from route (component) and subscribe when navigate back to the route (component)?

Angular 4.1.1 angular-cli 1.0.1

Hope I made it simple and clear enough.

1 Answers

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()
  });

}
Related