Angular Lazy Loading — Not Only For The Lazy

Angular Lazy Loading — Not Only For The Lazy

Two Types of Loading

When a user uses the Internet, they usually don’t think about how and when things are loaded as long as everything works fast and correctly. What’s happening from a developer’s perspective though is more complicated than that. 

Generally, we distinguish two types of loading in many programming languages and technologies, including Angular: eager and lazy loading. 

The eager loading means that we evaluate and do all calculations when the code is loaded. 

The lazy loading waits until the evaluation and calculation of the code are actually needed. 

Additionally, you can even preload components when it makes sense, but let’s put that aside for now.

Let’s see the example in the pseudocode: 


lazy variable array1 = [1, 20+20, 40*5, 200/2]   
eager variable array2 = [1, 40-20, 20*5, 200/100]   
function arraySum() {
  return array1 + array2;
} 

There’s code calculating the sum of two arrays. Each array is created with some initial data and calculations. The lazy variable remains unchanged. After the code is loaded the eager variable is calculated immediately and our variables look like that:


lazy variable array1 = [1, 20+20, 40*5, 200/2]   
eager variable array2 = [1, 20, 100, 2]   

The value is calculated after the evaluation of the lazy array, once the function was called and the need for array1 arose. The value of array2 is already calculated.


lazy variable array1 = [1, 40, 200, 100]   
eager variable array2 = [1, 20, 100, 2]   
function arraySum() {
  return array1 + array2;
} 

The code evaluation is located in array1.

As we can see here, the lazy load helps you save some calculations when you’re not sure if you ever need to call the data.  

Lazy Loading Modules in Angular

Angular doesn’t support lazy variables natively but it supports the lazy loading modules, which are way more important. Why? 

Let’s imagine you’re developing a big application, for example — a discussion forum. It includes public sites with discussion threads, some sites available for logged-in users and the administration panel. There are many modules for different routes. Surely you don’t want to load all the routes at once, but that’s what happens with eager loading, affecting the load uptime and decreasing the overall performance of the application. Fortunately, you can easily solve this problem using lazy loading. 

Before you see how to implement lazy loading, let’s look at its most important benefits:

  • The modules will be loaded on–demand.

Big parts of the code will not be initially loaded. It helps with performance, availability, debugging, and security. You decide when and what should be loaded. 

  • You have a logical split of the application.

When you want to load routes lazy, you need to have a clear split of which route is needed for the specific scenario. For example, it doesn’t make sense to load the admin dashboard when you access the main page as a not–logged–in user. You can have even more complicated construction saying that some modules should be preloaded, for instance, because they are usually the next step for a user registration process. Of course, the preloading only makes sense when we use lazy loading in the first place.

How to Create Lazy Loaded Modules and Test Them

To answer the question of how lazy loading in Angular works, let’s build a simple application to demonstrate and test it. I use Angular CLI to help me with it. 

First of all, I create a fresh Angular app with the routing: 

ng new lazy-loading-angular

ng new lazy-loading-angular

Then I create two components and routes for them: userDashboard and adminDashboard. The CLI helps us create lazy loading modules for them during their creation process.  


ng generate module userDashbaord --route userDashboard --module app.module
ng generate module adminDashbaord --route adminDashboard --module app.module

Then, you can observe two new components with their routing modules and routes to them included in the app-routing.module.ts file (our root module for routing).

The routes should look like this:

const routes: Routes = [
  {
    path: 'userDashboard',
    loadChildren: () =>
      import('./user-dashbaord/user-dashbaord.module').then(
        (m) => m.UserDashbaordModule
      ),
  },
  {
    path: 'adminDashboard',
    loadChildren: () =>
      import('./admin-dashbaord/admin-dashbaord.module').then(
        (m) => m.AdminDashbaordModule
      ),
  },
];

The most important thing is using loadChildren property instead of componentit’s everything you need to configure lazy loading. It points to the module of the component that should be lazy–loaded, when we call this route. 

Let’s take a look now at the hierarchy of the component module. There’s a module file for a user dashboard:


import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

import { UserDashbaordRoutingModule } from './user-dashbaord-routing.module';
import { UserDashbaordComponent } from './user-dashbaord.component';

@NgModule({
  declarations: [UserDashbaordComponent],
  imports: [CommonModule, UserDashbaordRoutingModule],
})
export class UserDashbaordModule {}

We import the user component here (so it belongs to this module), common module, and the local routing. Let’s see how it looks now:


import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { UserDashbaordComponent } from './user-dashbaord.component';

const routes: Routes = [{ path: '', component: UserDashbaordComponent }];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class UserDashbaordRoutingModule { }

Here we can see that the default empty path points to the user dashboard. Note that I don’t use loadChildren here, I simply use a component as the lazy load is done higher in the hierarchy. If you have some sub-routes, you can use lazy loading here as well. 

How can you verify lazy loading? How to make sure that it actually works?

You can simply start the application, open the network tab in the developer tools, and see if the modules are actually loaded when you need them. In the example, you go to the user or admin dashboard from the main page. 

When the app loads, you can see all loaded files. That’s how it looks like for this project:

Screenshot of all the loaded files in the app.

Now we navigate to the test routes and observe any changes. The modules should be loaded then. 

Screenshot of all the loaded files in the app after applying lazy loading.

As expected, the module has been lazy loaded and the component appears in the router-outlet.

It’s worth noting that once the lazy loading module has been loaded, it doesn’t need to be done again. It means that in the case of leaving this route and coming back in the same session, the cached value of the module is used. 

It’s as easy as that. Of course, it works not only for new routes but also for existing routes, you just need to redefine them to use loadChildren calling the right module file.

If you want to check it by yourself, the final version of the application is available here.  

A Quick Introduction to Preloading

If we use lazy loading, we may want to use the preloading. Pre-loading is a technique to request loading of the module before it’s needed as you foresee it will be needed shortly. 

For example, if you know from your statistics that a user goes from an admin dashboard to reports, so when a user is on the dashboard, we load a report page as well. There are two basic preloading types:

  • Preloading modules,
  • Preloading component data.

The preloading of modules requires a specific preloadingStrategy. If you want to preload all modules in the given root, you can use preloadAllModules from @angular/router. You can also use app-routing.module.ts after implementing the preloading for all modules.


import { NgModule } from '@angular/core';
import { RouterModule, Routes, PreloadAllModules } from '@angular/router';

const routes: Routes = [
  {
    path: 'userDashboard',
    loadChildren: () =>
      import('./user-dashbaord/user-dashbaord.module').then(
        (m) => m.UserDashbaordModule
      ),
  },
  {
    path: 'adminDashboard',
    loadChildren: () =>
      import('./admin-dashbaord/admin-dashbaord.module').then(
        (m) => m.AdminDashbaordModule
      ),
  },
];

@NgModule({
  imports: [
    RouterModule.forRoot(routes, {
      preloadingStrategy: PreloadAllModules,
    }),
  ],
  exports: [RouterModule],
})
export class AppRoutingModule {}

This strategy isn’t always the best choice. That’s why it’s actually highly recommended to create your own instead.

If there’s a need to preload some data, we need to create a new service. For example, by implementing Resolve. Once it’s added to the route the data is loaded in the background. We don’t need to do anything else here.  

Lazy Loading in Angular: Key Takeaways

It’s impossible to build modern big web applications without using lazy load. Nowadays, it’s a standard used everywhere even if users don’t notice it at first. When your application uses lazy loading and collects some statistics, it might be a good starting point to think which elements should be preloaded where.  Angular provides not only a lazy loaded module but also a mechanism to preload them as well. 

Don’t be afraid to load your app module lazily and do some experiments while at it. It may seem like a small gain of application performance but you’ll really appreciate it in the long term, especially in peak moments of using your product. 

Happy lazy coding of  {{ yourProductName }}

Looking for a reliable software development team?

We use cookies

We use cookies to make sure your website experience is as easy and as personal as possible. By accepting, you’re allowing them to do their job. Change your cookie preferences if you wish to.

Why? To analyze our visitor data, improve our website and show personalized content to our users. We want to give you a better experience of Massive Pixel Creation. Are you fine with this?