22

Lazy loading is a commonly used technique. However, in Angular it seems that the granularity level of this technique stops at the module level.

That means that you can have module main that loads in the browser, and then on special occasion module B and C and D can be loaded lazily.

Almost all tutorials on web explain that. However, is there a way to load components lazily?

Consider this simple example that user goes inside the application, and when clicks "invoices" link, URL changes to the new route /invoice/list and a progress bar shows loading, while the invoices component including HTML and JS is being loaded inside the browser, is registered dynamically with the main module, and is being shown in the relevant outlet. Is that possible with Angular?

  • have them as separate module – Aravind Jan 06 '18 at 06:17
  • 1
    you mean module per component? That's too overloaded. Not efficient. – mohammad rostami siahgeli Jan 06 '18 at 06:22
  • 2
    no, there is no straightforward to do that, but you can load a component class and then generate a module and component on the fly, [see this tutorial](https://blog.angularindepth.com/here-is-what-you-need-to-know-about-dynamic-components-in-angular-ac1e96167f9e#2b99) – Max Koretskyi Jan 06 '18 at 10:14
  • Maybe this will be added in angular 7, [see this issue](https://github.com/angular/angular/issues/23284) – M_Farahmand Jul 08 '18 at 14:20
  • Yes, as of Angular 9 with Ivy. See this [post](https://netbasal.com/welcome-to-the-ivy-league-lazy-loading-components-in-angular-v9-e76f0ee2854a). – ebhh2001 Jan 30 '21 at 05:00

4 Answers4

7

Lazy loading a component is not possible. Reason for this behavior is the structure of angular. Components in angular have to be part of a module. Angular module is a container where you define all the components, services, pipes e.t.c. present inside. While building the application dist, all the dependencies declared in the module are included to make a chunk (transpiled js code). All the directly imported modules together form the main chunk whereas modules marked as lazily loaded form a separate chunk that sits on the server untill the respective route is hit and a call for it is made from the client. Now components do not form a chunk and hence it is not possible

4

Currently (Angular 8 timeframe) there are two 3rd party libraries that make lazy-loading of components easier:

Note: both of these libraries are built upon NgModuleFactoryLoader, which is deprecated in Angular 8, but there is no alternative yet ... 


The Angular team has announced that lazy loading of components should become easier with Ivy (Angular 9?), but it is not clear yet how this will look like...

jbandi
  • 17,499
  • 9
  • 69
  • 81
  • Igor Minar presented how the lazy loading of components will look like in Ivy at this year’s AngularConnect. Regarding the NgModuleFactoryLoader being deprecated: see my latest blog post here: https://juristr.com/blog/2019/10/lazyload-module-ivy-viewengine/ – Juri Oct 01 '19 at 22:19
0

A bit hacky ... but possible (future versions of Angular (beyond ver. 7) will implement an easier apporach)

https://blog.angularindepth.com/dynamically-loading-components-with-angular-cli-92a3c69bcd28

Boban Stojanovski
  • 892
  • 13
  • 26
0

Update, with Angular 9 and Ivy it is possible and relatively easy. First of all you need to have some anchor in your app where you want to load your component, it could be named templateRef if you want multiple different components at the same time or ng-template for single one. To do that (i will use ng-template) in your component.html:

<ng-template>

component.ts:

export class ExampleComponent {
    @ViewChild(TemplateRef, { read: ViewContainerRef })
    private anchorRef: ViewContainerRef;

    constructor(
        private readonly componentFactoryResolver: ComponentFactoryResolver
    ) {}

    private async loadComponent() {
        this.anchorRef.clear(); // clear if some components are already there
        const { component } = await import(
            'path to your component'
        );
        const componentFactory = this.componentFactoryResolver.resolveComponentFactory(
            component
        );
        // We could attach @Input and react to @Output by using below instance if needed.
        // Remember we should handle the ngOnChanges lifecycle hook.
        // For that implementation needed.
        this.anchorRef.createComponent(componentFactory);
    }
}

All that is left is basically call loadComponent when it's needed;

If you need some modules, for ex you want to use Angular Material. You need to create simple ngModule without export inside your lazyLoaded component. Like this:

@Component({...})
export class Component implements OnInit {
    constructor() {}

    ngOnInit() {}
}

@NgModule({
    declarations: [Component],
    imports: [MatIconModule],
})
class TasksListModule {}

Also, you could follow the approach Angular team has in their documentation: https://angular.io/guide/dynamic-component-loader

  • Can I follow this approach to lazy-load custom form controls that are part of a bigger reactive form? In other words, are there any restrictions on what kind of components can be lazy-loaded as described above? – ebhh2001 Jan 29 '21 at 19:08
  • 1
    Basically, lazy-loaded component is just the same component as any other. You just use the factory Angular use under the hood for any other component and Async function to load the file itself. So no restrictions. Just be careful as the component might not be on page when you try to interact with it programmatically – Alexander Ismagulov Jan 31 '21 at 13:31
  • Why is this at 0 votes? Does it not work? – JPS Nov 01 '21 at 10:27