Angular 9: Lazy Loading Components

John Papa - Feb 16 '20 - - Dev Community

Angular 9 has some pretty awesome new features. The runtime, code-name Ivy, opens the doors to things like making lazy load Angular components more straightforward than ever.

This article shows you how to lazy load with Angular 9 and provides the code and resources along the way.

Lazy Loading Components in Angular 9

1 - Create a New App

Create a new Angular app using the Angular CLI command below. The following code will generate an app with as few files as you can get.

ng new lazy-demo 
  --minimal 
  --inline-template 
  --inline-style 
  --routing=false 
  --style=css

This command will create a new angular app in a folder named lazy-demo

  • --minimal removes removes testing frameworks
  • --inline-template puts all component templates in the .ts file
  • --inline-styles puts all component styles in the .ts file
  • --routing=false does not add any routing
  • --style=css specifies to use CSS

2 - Create Lazy Components

Create two new components named lazy1 and lazy2.

ng g c lazy1 --flat --skip-import --skip-selector
ng g c lazy2 --flat --skip-import --skip-selector

These commands will create the two new components in files named lazy1.component.ts and lazy2.component.ts, respectively. We won't want either component to be declared in a module, since we want to lazy load them. If we declare them in a module, then Angular will eagerly load them.

We're also not creating the selectors since we won't be referring to them in a template directly. Instead, we'll load them dynamically.

3 - Lazy Load the Components

Add the following code to the file app.component.ts. Notice the constructor injects a ViewContainerRef (a place to put our components) and a ComponentFactoryResolver (this creates our components in code).

export class AppComponent {
  title = 'lazy-comp';

  constructor(
    private viewContainerRef: ViewContainerRef,
    private cfr: ComponentFactoryResolver
  ) {}

  async getLazy1() {
    this.viewContainerRef.clear();
    const { Lazy1Component } = await import('./lazy1.component');
    this.viewContainerRef.createComponent(
      this.cfr.resolveComponentFactory(Lazy1Component)
    );
  }

  async getLazy2() {
    this.viewContainerRef.clear();
    const { Lazy2Component } = await import('./lazy2.component');
    this.viewContainerRef.createComponent(
      this.cfr.resolveComponentFactory(Lazy2Component)
    );
  }
}

The getLazy1 function clears the container. This is important is we only want to show one of the lazy-loaded components at a time. If we did not clear the container, every time we lazy load components, they would be displayed one after another.

Next, we import the components, lazily, using the await import syntax.

Finally, we create the component in the container.

4 - Adding Buttons to Lazy Load

Modify the template in app.component.ts, as shown below. This adds buttons that will lazy load each component when clicked.

  template: `
    <div>
      <div>Hello World! This is the {{ title }} app.</div>
      <button (click)='getLazy1()'>lazy 1</button>
      <button (click)='getLazy2()'>lazy 2</button>
    </div>
  `

5 - Watch it Lazy Load

Now run the app with ng serve and browser to http://localhost:4200. After the app loads, open the browser's developer tools. Then clear the network traffic, so we can see when the components are lazy-loaded.

When you click one of the buttons, notice that the associated component I displayed and the network traffic shows the component is lazy loaded.

6 - What if Lazy Loaded Components Have Children

This is cool, but what if a lazy-loaded component has child components of its own? Imagine that Lazy2Component needs to show two other components named Lazy2aComponent and Lazy2bComponent. We'll need to generate these two components, and once again, make sure we do not declare them in a module.

ng g c lazy2a --flat --skip-import --skip-selector
ng g c lazy2b --flat --skip-import --skip-selector

Now modify the Lazy2Component to load it's two child components. We'll once again use the ViewContainerRef and ComponentFactoryResolver.

However, this time we will not lazy-load the children. Instead, we'll create the child components in the ngOnInit and import them synchronously.

What's the difference? Well, in this example, these child components will load in the same bundle as their parent, Lazy2Component.

Modify your Lazy2Component code, as shown below.

import {
  Component,
  ViewContainerRef,
  ComponentFactoryResolver,
  OnInit
} from '@angular/core';
import { Lazy2aComponent } from './lazy2a.component';
import { Lazy2bComponent } from './lazy2b.component';

@Component({
  template: `
    <p>lazy2 component</p>
  `
})
export class Lazy2Component implements OnInit {
  constructor(
    private viewContainerRef: ViewContainerRef,
    private cfr: ComponentFactoryResolver
  ) {}

  ngOnInit() {
    const componentFactorya = this.cfr.resolveComponentFactory(Lazy2aComponent);
    const componentFactoryb = this.cfr.resolveComponentFactory(Lazy2bComponent);
    this.viewContainerRef.createComponent(componentFactorya);
    this.viewContainerRef.createComponent(componentFactoryb);
  }
}

7 - Run the App

Now run the app again and browse to http://localhost:4200. Or go back to the browser if you never stopped serving it.

Open the browser's developer tools, go to the Network tab, and clear the network traffic.

Notice that when you click on the button to load the Lazy 1 component that the bundle for that component is passed, and Lazy 1 is displayed.

When you click the button to load Lazy 2 its bundle is passed, and Lazy 2, Lazy 2a, and Lazy 2b are all displayed.

The bundle sizes for Lazy 1 and 2 are different, too. Lazy 1 only has a single component, so it is smaller than Lazy 2 (which contains three components).

Should You?

So now you know how to lazy load a component with Angular 9. You can lazy load a component and have its children in turn lazily load or eagerly load. But you could also do this with a module (specifically an NgModule). So what do you do? Lazy loading a component helps support scenarios where you want to access features without routing. Lazy loading of modules helps when you want to access features with routing. But should that line be so distinct? Perhaps that line will blur as time moves forward. There are no warning signs here, just things to consider before entering this arena.

Another scenario might be when you want to load component dynamically based on user profile or a workflow. You could dynamically load (eagerly or lazily) one or more components.

Learn More

These examples should be able to help you get started with lazy loading components dynamically, with or without children. If you want to learn more, check out these resources:

  1. Dynamic Component Loader
  2. 7 new features in Angular 9.
  3. VS Code editor
  4. Angular Essentials Extension for VS Code
  5. Angular Language Service for VS Code
  6. Angular Lazy Load Demo source code
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .