Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Act when lazy loaded module is destroyed

I have an Angular 6 application and I am lazy loading a module and passing some data via the router. Once in the module, I call a method within a shared service to which I pass this data (config stuff).

I need to call a method when that module is destroyed (essentially, when the user leaves that path), but I want to call it only once, so I want to avoid having to watch the route changing constantly as this will be expensive. The reason is I need to reset the shared service configuration. Basically, I have certain configuration data for the application, and I need to override it for the lazy-loaded module, but put it back once the user is not in the module.

I am trying to call the function on the OnDestroy hook of the module but it is not triggered.

Routing module:

const appRoutes: Routes = [
    {
        path: 'lazy',
        loadChildren: 'lazy.module#LazyModule',
        data: {
            test: 'data'
        }
    },
    {
        path: 'home',
        loadChildren: 'home.module#HomeModule',
    },
{
        path: '**',
        redirectTo: '/home'
    }
]

Lazy loaded module:

export class LazyModule implements OnDestroy {

    constructor() {
        console.warn('LazyModule launched!'); // I see this
    }

    ngOnDestroy() {
        console.warn('destroyed'); // This is not triggered
    }

}
like image 712
MartaGalve Avatar asked Aug 29 '18 00:08

MartaGalve


1 Answers

As of Angular 6 modules never unload.

The router currently doesn't check if a module has been destroyed after lazy loading it. So even if you get the NgModuleRef and call destroy manually the router still thinks it's loaded. So it won't lazy load it a second time.

The router just loads the module but does not manage it's life-cycle. Even if you could destroy a module it would not free much memory. Lazy loaded modules are managed by WebPack bundles that are loaded with SystemJS. Once they are loaded they stay in memory. Even if Angular destroy the module instance the source code for the bundle is still in SystemJS's memory cache of loaded bundles.

This problem extends to vendor libraries. If you have a lazy loaded module that uses a third-party graphics library like D3, then that vendor library will be loaded and you can't unload it.

If you need to have providers that exist only for specific routes, then you should be using view providers.

@Component({
     ...
     viewProviders: [
         LazyFeatureService
     ]
})
export class MyLazyComponent {}

When you use the above component as a router component, then the service LazyFeatureService is created when the module is lazy loaded, but when the component is destroyed the service is also destroyed. The next time the user visits the lazy route the service will get created again.

Update:

The reason is I need to reset the shared service configuration. Basically, I have certain configuration data for the application, and I need to override it for the lazy-loaded module, but put it back once the user is not in the module.

You can achieve this by using the canActivate and canDeactivate handlers in the route.

In your route config for the lazy module create a top-level route to handle activations.

const routes: Routes = [
    {
        path: '',
        canActivate: [ConfigActivator],
        canDeactivate: [ConfigActivator],
        children: [
            // move the routes here
        ]
    };

You can then define the activator like this.

 @Injectable()
 export class ConfigActivator implement CanActivate, CanDeactivate<any> {
       public constructor(private config: MyConfigService) {
       }

       public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
           this.config.lazyModuleLoaded();
           return true;
       }

       public canDeactivate(component: any, currentRoute: ActivatedRouteSnapshot, currentState: RouterStateSnapshot, nextState?: RouterStateSnapshot): boolean {
           this.config.lazyModuleUnloaded();
           return true;
       }
 }

The above will call methods on the service to tell it when it should update the config based upon the change in router state. Since this is on a top-level route for the lazy module it will only be triggered when the route is activated, and when the route leaves that path.

like image 83
Reactgular Avatar answered Oct 21 '22 05:10

Reactgular