Module Routing Strategies in Angular

Open on Github: https://github.com/niharika293/angular-module-strategies

Loading of a module can be achieved in 3 ways -

  • Eagerly Loading {Default}: This will load all the modules all at once, even before our app starts.

    -> For small apps, it's a very simple thing to do, but in my opinion, if the app size grows & when the complexity increases, we should opt out of this approach.

    -> When the app grows -> Bundle size will increase & hence it'll impact the performance of our app by making it slow.

  • Lazy Loading: This is way much better than eagerly loading, as it loads the modules on demand, i.e. when a particular route is accessed only then does the relevant module get loaded.

    -> Modules are loaded asynchronously here.

    -> Don't forget to remove the lazy loaded module from the app module for maximum utilization of this amazing feature!

  • Pre-loading: This is the ultimate game changer! It's an advanced practice that changes the entire A-Game of loading the angular app & makes it super duper fast!

    -> It loads the modules right after the app starts, that too in the background! Sounds confusing? Well, It isn't!

    -> Since, we know that it isn't a good practice to load all the modules at once in case of eagerly loading, we thought that maybe lazy loading is the best, but let me tell you here that it's also dependent on the routes to fetch the data, load the components! And heavier the components, the more time it'll take to load the page / UI.

    -> So, what can be done now? Yes, the answer is Pre-loading! By doing pre-loading we can save lots of wait time & have the views on the go!

    -> We can pre-load a component/modules (both lazy-loaded & eagerly-loaded}

Flow : Source - GeeksForGeeks.org . All in all, only our app module gets loaded eagerly before the app starts, but the feature modules can be loaded lazily / pre-loaded way.

Implementation :

  1. Default / Eagerly loading -> To do this, in your routing file, simply mention the path of the route & the component name.

    • This will be eagerly loaded.

  2. Lazy Loading -> Instead of defining the component here, use the "loadChildren" method here. {syntax, meaning of function syntax, for rroot, for child }

    -> Shortcut command to generate lazy loading module : ng g m module_name --route route_name app.module

    -> It'll make a customers module, which is loaded only when the route "customers" is accessed, & we're mentioning app.module here which means that it'll add a reference of the customers route in app-routing module.

    -> Just Keep in mind that, just because we've mentioned app.module in the command, this will not add the customers module in the imports array of routes in our app.module, since it's lazy loaded.

    -> Also, in the network tab, you'll see that the bundle of this customer module gets loaded on demand.

  3. Pre-loading: There are 4 ways to have pre-loading :

    • Module level {Single module / all modules}

      -> Add more than 1 lazy-loaded module in your app.

      -> We're now pre-loading all the modules at once. Go to app-routing.module.ts file & enable the pre-loading strategy as preloadAllModules.

      ->

      -> This will eagerly load the default page, apart from that once we're surfing, the other modules get pre-loaded.

      -> I've logged the consoles in the constructor in their routing module.ts files so that we can see how our components work!

      -> Now, when you click on these hyperlinks the pages will open super-duper fast & thus a smoother experience.

    • Standalone app level { done in app.config.ts file }

      -> Source: Angular doc.

      ->Standalone components - introduced in angular 14 { which I need to explore, hence dropping the reference from the official doc}

    • Customized pre-loading { some modules to be pre-loaded & others to be lazy-loaded}

      -> Now, we want only the "products" module to be pre-loaded. So, we'll customize the angular's preload method which initially looks like this.

      -> To pre-load the module, pass in a data object with some property {can be any name} as true.

        { path: 'products', data: {preload : true}, loadChildren: () => import('./products/products.module').then(m => m.ProductsModule) }
      

      -> Generate a service in which we can implement this method. If the route is passing some data with a preload object then we're returning the same function that this method returns, otherwise null.

      -> In the app-routing module, instead of preloadAllModules, use this service!

      
        @NgModule({
          imports: [RouterModule.forRoot(routes,{
            preloadingStrategy : CustomPreloadingService
          })],
          exports: [RouterModule]
        })
      

      -> Now, only the app & product modules are loaded!

    • Component level - At this level, we can use "resolvers" which can pre-fetch the route data / send some data to the route even before they're activated. They are also useful in scenarios where we want to block parts of our UX when the necessary data is not yet loaded completely. {Basically, we're sending some data to preload the component}

      -> Make a resolver within the products module that implements the interface resolve.

      -> ng g resolver products/products-resolver.In the resolver file, have used some mock data & returning it. In real-time scenarios, the data can be fetched via API calls.

        export class ProductsResolverResolver implements Resolve<Campaign[]> {
          resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Campaign[]> {
            // returning mock data with 1s delay to conceptualise this better!
            return of(campaignsMock).pipe(delay(1000));
          }
        }
      

      -> Now, in the products-routing module, we'll make a path & add our resolver.

         {path : 'campaign-details', component : CampainProductsComponent, resolve : {
            products : ProductsResolverResolver
          }}
      

      -> Now, we're going to access & display the resolved data in our campaign-products component using activatedRoute.

        export class CampainProductsComponent implements OnInit {
      
          campaignsList : any = [];
          constructor(private route : ActivatedRoute) { }
      
          ngOnInit(): void {
           console.log("this.route", this.route);
           if(this.route.snapshot.data['products']){
              this.campaignsList = this.route.snapshot.data['products'];
           }
           console.log("campaignsList", this.campaignsList);
          }
      

      -> Since the component has been able to fetch the resolved data from the route, only then does the component get loaded.

      Thank you so much for popping in here! That's all folks! For any Query / Suggestion/feedback, connect with me over LinkedIn. :)

0
Subscribe to my newsletter

Read articles from Niharika Gurnani directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Niharika Gurnani
Niharika Gurnani