10

I have several modules that I want to lazy load dynamically, I am upgrading from v8 to v9, with the version 9 of angular the logic concerning modules seems to have changed. What is the best way of doing it ?

0

1 Answer 1

18
  1. Component with no module

If we want to lazy-load a component dynamically (with no module), then we can use the same pattern as with routes :

// <ng-template #myContainer></ng-template>    
@ViewChild('myContainer', { read: ViewContainerRef }) container: ViewContainerRef;
  
const { MyLazyComponent } = await import('./path/to/lazy/component');
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(MyLazyComponent);
const { instance } = this.container.createComponent(componentFactory);
  1. Modules

If the component depends on other services / components then we need to load the full module. Because it will be lazy loaded (not compiled initially) we need to run the compilation "manually". It is still easier than previous tricks used on previous versions of Angular. Here is a solution.

We can create a service that store module references, and load the component into a container (given a module ID and a container reference).

import { Injectable, Compiler, Injector } from '@angular/core';

@Injectable({
    providedIn: 'root'
})
export class LazyComponentService {

    private componenttRefs = {
        myFirstLazyModuleId: import('../path/to/first/lazy/module/component.module'),
        mySecondLazyModuleId: import('../path/to/second/lazy/module/component.module')
    };

    constructor(
        private compiler: Compiler,
        private injector: Injector,
    ) { }

    async loadComponent(moduleId, container) {

        let ref;
        try {
            const moduleObj = await this.componenttRefs[moduleId];
            const module = moduleObj[Object.keys(moduleObj)[0]];
            const moduleFactory = await this.compiler.compileModuleAsync(module);
            const moduleRef: any = moduleFactory.create(this.injector);
            const componentFactory = moduleRef.instance.resolveComponent();
            ref = container.createComponent(componentFactory, null, moduleRef.injector);
        } catch (e) {
            console.error(e);
        }
        return ref;

    }

}

Modules need to be compiled. We do it by calling resolveComponentFactory inside each module's constructor :

@NgModule({
  imports: [
    MyModules...
  ],
  declarations: [
    MyFirstLazyComponent
  ]
})
export class MyFirstLazyComponentModule {

  constructor(private componentFactoryResolver: ComponentFactoryResolver) { }

  public resolveComponent(): ComponentFactory<MyFirstLazyComponent> {
    return this.componentFactoryResolver.resolveComponentFactory(MyFirstLazyComponent);
  }

}

And then the magic is up, you can dynamically lazy load a component to a container :

  const myFirstLazyComponent = await this.lazyComponentService.loadComponent(myFirstLazyModuleId, containerRef);
Sign up to request clarification or add additional context in comments.

6 Comments

the line const componentFactory = moduleRef.instance.resolveComponent(); Throws: Error: ASSERTION ERROR: NgModule '[object Promise]' is not a subtype of 'NgModuleType'. [Expected=> null != null <=Actual] any ideas?
Are you sure that you are loading a module and not a component ?
Sorry, turns out Stackblitz can't handle ng 10 and also ivy rendering. You get that error with v10 packages installed. Awesome solution btw.
do you have any stackblitz of the same? can you please share it if you have
I know this is an old post, but I am facing the same issue with lazy loading of modules while migrating from AngularV8 to V9 and moving upwards to Angular15. This solution seems to load the lazy module at the initial App module itself.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.