Search code examples
angularlazy-loadingangular-routingdynamic-routingangular-lazyloading

Angular dynamic lazy loading of components


Problem

I have an Angular application where components are registered under a certain name to a global map using decorators. This name can then be used to dynamically create a route for this component (at runtime).

E.g. a component is registered with name A, then dynamically a route can be created with a path /route/to/A which displays the component A.

This process works fine but all the components need to be in the main bundle for this to work.

As the app is growing bigger lazy loading becomes more and more necessary. Is there any possible way to create this kind of lazy loading components dynamically? I imagine that all of the components that can be dynamically added to routes need to have their own lazy loaded module. What I am now missing is how these modules are integrated with dynamic routes (which are only known at runtime) to the lazy loading process.

I am not necessarily looking for exact source code but also any pointers to potential approaches/similar projects would be incredibly helpful.

Possible approach

One approach I can imagine is through adding the lazy loaded module under a fixed url and then dynamically creating redirects to this path

e.g.

export const routes: Routes = [
  {
    path: "lazy-loaded-static",
    loadChildren: () =>
      import("./lazy-loaded/lazy-loaded.module").then(
        (m) => m.LazyLoadedModule
      ),
  },
  // this is added dynamically later
  {
    path: "lazy-loaded-dynamic",
    redirectTo: "lazy-loaded-static",
  },
]

The issue with this is that the url will always show /lazy-loaded-static even when visiting /lazy-loaded-dynamic. I would like lazy-loaded-dynamic to stay.


Solution

  • I have found a solution by looking for the existing lazy loaded route and then re-using the loadChildren function of that one for the new dynamic route:

    In the app.routing.ts

    /**
     * Static routes that are added in the AppModule
     */
    export const allRoutes: Routes = [
       {
        path: "lazy-loaded-static",
        loadChildren: () =>
          import("./lazy-loaded/lazy-loaded.module").then(
            (m) => m.LazyLoadedModule
          ),
      },
    ];
    
    /**
     * Main app RouterModule with centrally configured allRoutes.
     */
    export const routing: ModuleWithProviders<RouterModule> = RouterModule.forRoot(allRoutes);
    

    In the a service that adds new routes at runtime

    export class DynamicRoutesService {
      constructor(private router: Router) {}
    
      /**
       * Initialise route dynamically while respecting existing routes.
       */
      initRouting() {
        const existingRoutes = this.router.config;
        const lazyLoaded = existingRoutes.find(
          (route) => route.path === "lazy-loaded-static"
        );
        const newRoute = {
          path: "lazy-loaded-dynamic",
          loadChildren: lazyLoaded.loadChildren,
        };
    
       this.router.resetConfig([newRoute, ...existingRoutes]);
      }
    }