0

I have a library project in which I have a single module along with few components. In my actual application I want to load the library module dynamically and render the components. Everything working fine in development mode, but when I run my application with production mode always getting empty array of components from factory (with moduleFactory.componentFactories). Below is my code. Can anyone help me on this please. I am using Angular 9.1.0.

import { Injectable, Type, Compiler, Injector, ComponentFactory, NgModuleFactory } from '@angular/core';
import { Observable, of } from 'rxjs';
import { delay, tap } from 'rxjs/operators';
import { ApptorCustomCompHostComponent } from '@apptor/corecomps';

@Injectable({
  providedIn: 'root',
})
export class LazyLoaderService {
  private modules: Map<string, any> = new Map();
  private componentRegistry: any[] = [
    { type: "textbox", className: "ApptorCorecompsWrapperComponent", moduleName: "ApptorCorecompsModule" }
    ];
  constructor(private compiler: Compiler, private injector: Injector) {

  }

  public async getComponentFactory(componentType: string): Promise<ComponentFactory<any>> {
    let compInfo = this.componentRegistry.find(c => c.type === componentType);
    if (compInfo) {
      let factories = this.modules.get(compInfo.moduleName);
      if (!factories) {
        await this.loadModule(compInfo.moduleName);
        factories = this.modules.get(compInfo.moduleName);
      }
      let componentFactory = factories.find(f => compInfo.className === f.componentType.name);
      return componentFactory;
    }
    return null;
  }


  async loadModule(moduleName: string) {
    switch (moduleName) {
      case 'ApptorCorecompsModule':
        this.loadModuleInternal(moduleName, await import('@apptor/corecomps').then(m => m[moduleName]));
        break;
      case 'lazy':
        this.loadModuleInternal(moduleName, await import('@apptor/corecomps').then(m => m[moduleName]));
        break;

    }
  }

  private loadModuleInternal(moduleName: string, moduleType: Type<any>) {
    const moduleFactory = this.compiler.compileModuleAndAllComponentsSync(moduleType);
    const componentFactories = moduleFactory.componentFactories;
    this.modules.set(moduleName, componentFactories);
    console.log("module loaded is: ", moduleName);
  }
}
Buddha
  • 185
  • 1
  • 15
  • 1
    I have followed this tutorial to load routes lazily as well as dynamically. Its now old, but still valid https://indepth.dev/dynamically-loading-components-with-angular-cli/. Also refer to official Angular site on this too https://angular.io/guide/dynamic-component-loader. May be it will help you. – Syed Sharique Jul 01 '20 at 18:45

1 Answers1

0

We made it working as below. Hope this may help others.

import { Injectable, Compiler, Injector, ComponentFactory, ComponentFactoryResolver } from '@angular/core';
import { ReplaySubject } from 'rxjs';

@Injectable({
  providedIn: 'root',
})
export class LazyLoaderService {
  private modules: Map<string, ReplaySubject<any>> = new Map();
  private componentRegistry: any[] = [
    { type: "textbox", className: "ApptorCorecompsWrapperComponent", moduleName: "ApptorCorecompsModule" }
  ];
  constructor(private compiler: Compiler, private injector: Injector,
    private factoryResolver: ComponentFactoryResolver) {

  }

  public async getComponentFactory(componentType: string): Promise<ComponentFactory<any>> {
    //this.loadComponent();
    const compInfo = this.componentRegistry.find(c => c.type === componentType);
    if (!compInfo)
      throw new Error("Invalid component type:" + componentType);

    let modSubject = this.modules.get(compInfo.moduleName);
    if (!modSubject) {
      modSubject = this.loadModule(compInfo.moduleName);
    }
    let ret: Promise<ComponentFactory<any>> = new Promise((resolve, reject) => {
      modSubject.subscribe(async (moduleRef) => {
        //await this.compileModule(mod);
        const compType = moduleRef.instance.controls[compInfo.className];
        if (compType) {
          let componentFactory = moduleRef.componentFactoryResolver.resolveComponentFactory(compType);
          // let componentFactory =  this.factoryResolver.resolveComponentFactory(compType);

          resolve(componentFactory);
        } else {
          console.error("Component :" + compInfo.className + " not found");
          reject(null);
        }
      });

    });
    return ret;


  }

  public loadModule(moduleName): ReplaySubject<any> {
    //let mod:any = null;
    let mod: ReplaySubject<any> = new ReplaySubject();

    switch (moduleName) {
      case 'ApptorCorecompsModule':
        import('@apptor/corecomps').then(async m => {
          let mm = m[moduleName]
          mm = await this.compileModule(mm);
          mod.next(mm);
          //return mm; 
        });
        break;
    }
    if (mod) {
      this.modules.set(moduleName, mod);
      return mod;
    }
    else {
      console.error("Failed to load module: " + moduleName);
      throw new Error("Failed to load module: " + moduleName);
    }
  }
  private async compileModule(module: any) {
    let ret: Promise<any> = new Promise((resolve, reject) => {
      this.compiler.compileModuleAsync(module).then(factory => {

        const moduleRef = factory.create(this.injector);
        resolve(moduleRef);
      });
    });
    return ret;
  }
}

Buddha
  • 185
  • 1
  • 15