0

Update

From https://stackoverflow.com/a/43673630/828547

[...]with LazyLoaded modules, route.routeConfig.path is empty when executing shouldAttach

Using explicitly defined additional developer-defined data rather than route.routeConfig.path works.

Original Question

When implementing RouteReuseStrategy to provide a custom route reuse strategy, I've observed.

  1. OnInit and the constructor are called when entering a component that has previously been stored for reuse
  2. OnDestroy is NOT called when leaving a component that has previously been stored for reuse

Intuitively, it makes sense that OnDestroy is not called for a component that should be reused later on.

However, it seems counter-intuitive that the constructor and OnInit are called for components that are being reused. It's common to place initialization code in these functions, and so the component appears new each time.

There's no shortage of tutorials on RouteReuseStrategy (1, 2, 3, 4) , but I can't find anything confirming whether my observations are correct.

In the implementation of the RouteReuseStrategy interface below, MyComponent is stored for reuse. According, its OnDestroy hook is NOT called when navigating away from the component. However, upon reentering the component, its constructor and OnInit hook are called.

import { RouteReuseStrategy, ActivatedRouteSnapshot, DetachedRouteHandle } from '@angular/router';

export class CustomRouteReuseStrategy implements RouteReuseStrategy {

    private storedRoutes = new Map<string, DetachedRouteHandle>();

    /**
     * Determines if this route (and its subtree) should be detached to be reused later.
     * If shouldDetach returns true, then we need to store the route as we want to reuse later.
     * Called when leaving a route.
     */
    shouldDetach(route: ActivatedRouteSnapshot): boolean {
        if (!route.routeConfig?.path) {
            return false;
        }
        // Prevents the MyComponent component from being destroyed (OnDestroy not called).
        return ['my-component'].indexOf(route?.routeConfig?.path) > -1;
    }

    /**
     * Stores the detached route. Called when leaving a route.
     */
    store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
        if (!route.routeConfig?.path) {
            return;
        }
        const path = this.getPath(route);
        this.storedRoutes.set(path, handle);
    }

    /**
     * Determines if this route (and its subtree) should be reattached.
     * If shouldAttach returns true, we want to reuse a previously stored route instead of building new components.
     * Called when entering a route.
     */
    shouldAttach(route: ActivatedRouteSnapshot): boolean {
        if (!route.routeConfig?.path) {
            return false;
        }
        const path = this.getPath(route);
        return this.storedRoutes.has(path);
    }

    /**
     * Retrieves the previously stored route.
     */
    retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null {
        if (!route.routeConfig?.path) {
            return null;
        }
        const path = this.getPath(route);
        return this.storedRoutes.get(path) || null;
    }

    /**
     * Determines if this route (and its subtree) should be detached to be reused later.
     * Called everytime we navigate between routes.
     */
    shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
        // Reuse the route if the new route has the same config as the previous one.
        return future.routeConfig === curr.routeConfig;
    }

    private getPath(route: ActivatedRouteSnapshot): string {
        if (route.routeConfig !== null && route.routeConfig.path !== null) {
            return route.routeConfig?.path || "";
        }
        return "";
    }
}

Questions

  1. Should constructor and OnInit be called for components that are reattached (reused)?
  2. If answer to 1. is "yes" - where should initialization code be placed such that it's run only when the component is NOT being reused?

Stackblitz https://stackblitz.com/edit/angular-ivy-9fs3tg?file=src/app

To mirror my application the stackblitz has the following module hierarchy

- app
  - home
    - manager
    - detail

Each module has routing

  • / loads AppComponent
  • /home loads HomeComponent
  • /home/manager loads ManagerComponent
  • /home/detail loads DetailComponent

The custom routing strategy reuses the ManagerComponent so that upon returning to it, the list of details is not re-rendered.

enter image description here

enter image description here

To recreate the issue:

  1. Go to /home/manager
  2. Open the Console provided by Stackblitz to see the logging
  3. Click on /home/detail/1
  4. Look in the console and see that -----> stored route with path "manager" is logged
  5. Click "back to manager" and see that ManagerComponent.constructor and ManagerComponent.ngOnInit are logged.

The logging shows that the route was attached

  • shouldAttach(manager)
  • -----> Yes!
  • retrieve(manager)
  • -----> retrieved route with path "manager"

If the route was attached, why are the constructor and OnInit of the manager component called?

Jack
  • 10,313
  • 15
  • 75
  • 118
  • Hey @Jack, the `constructor` and `OnInit` should not trigger on a reused route as you can see in this StackBlitz https://stackblitz.com/edit/angular-upbnpo?file=src%2Fapp%2Fpages%2Fhello.component.ts – mikegross Oct 08 '21 at 16:16
  • @mikegross Thanks for confirming. I'll create a Stackblitz and add it to my question, because I can clearly see the component being attached AND reinitialized – Jack Oct 08 '21 at 16:36
  • @mikegross I've updated my question with a stackblitz: https://stackblitz.com/edit/angular-ivy-9fs3tg?file=src/app – Jack Oct 08 '21 at 17:18
  • https://stackoverflow.com/questions/42383546/angular2-doesnt-work-custom-reuse-strategy-with-lazy-module-loading solved my problem. – Jack Oct 09 '21 at 09:53
  • I was researching this as we speak! Good thing you got it solved, it should have been obvious from the start that this was the direction to take! I learned something today :) thanks! – mikegross Oct 09 '21 at 09:59

0 Answers0