'Angular components loaded from different module not rendered in dynamic compiled component

Our SaaS need to offer different home pages for different customers. Our approach is to have the different templates in the database. We also have components defined in a different module that must be used in the different home pages. So we're creating the home-page component dynamically, inheriting from the default one.

We're in Angular 13

It works in the dev, but not in prod with AOT. We don't get any error, and the interpolations in the dynamic component are processed, but the components coming from a different module are not rendered, it looks like they are not compiled, that is, treated as plane HTML tags.

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, AfterContentInit {

  title = 'dynamic';
  static factory: ComponentFactory<any> | undefined;
  template = "<h1>Component loaded dynamically</h1> <div>Below should be rendered a component loaded from another module</div><app-widget-component #theComponent></app-widget-component>";
   
  @ViewChild(AnchorDirective, { static: true }) anchorHost: AnchorDirective | undefined;  

  ngOnInit(): void {    

  }

  constructor(public _compiler: Compiler,
    public injector: Injector) {

  }

  ngAfterContentInit(): void {
    this.loadContent(this.template, "");
  }

  loadContent(content: string, javascript: string) {


    const cmpClass = class DynamicComponent extends DynamicComponentComponent {
      text: string = "Text";

      constructor(injector: Injector) {
        super(injector)
      }
    };

    (cmpClass as any).ctorParameters = () => [{ type: Injector }];
    
    const metadata = new Component({
      selector: "compiled-at-runtime",
      template: content
      // todo: check styles and other options
    });

    const decoratedCmp = Component(metadata)(cmpClass);

    const moduleDef = NgModule({
      imports: [WidgetModuleModule,CommonModule, RouterModule, FormsModule],
      declarations: [decoratedCmp]
    })(class DynamicHtmlModule { });

    const moduleWithComponentFactory = this._compiler.compileModuleAndAllComponentsSync(
      moduleDef
    );    

    let factory = moduleWithComponentFactory.componentFactories.find(
      x => x.selector === "compiled-at-runtime"
    );

    let viewContainerRef = this.anchorHost!.viewContainerRef;
    const injector = Injector.create({
      providers: [],
      parent: viewContainerRef.injector
    });

    const componentRef = viewC  
    
  } 
}

In prod we got :

Production/AOT

And in dev:

Dev



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source