How do you get the module of a component that's been dynamically loaded in Angular v13?
Asked Answered
H

2

5

In v12 you could use the following code to load/import a component dynamically, access its NgModule through ComponentFactory and then add it to the DOM using ViewContainerRef.

const injector: Injector = /* injected */
const cfg: ComponentFactoryResolver = /* injected */
const module = await import('path/to/component/file');
const componentDef = module['MyComponent'];
const cf: ComponentFactory = cfg.resolveComponentFactory(componentDef);
const compModule: NgModuleRef = (cf as any).ngModule;

const container: ViewContainerRef = /*retrieved using @ViewChild*/
container.createComponent(cf, null, injector, compModule)

As of v13 ComponentFactory (docs) and ComponentFactoryResolver (docs) are now labeled as deprecated and a new signature has been added to ViewContainerRef.createComponent which accepts a componentType: Type<C> as the first argument.

abstract createComponent<C>(componentType: Type<C>, options?: { index?: number; injector?: Injector; ngModuleRef?: NgModuleRef<unknown>; projectableNodes?: Node[][]; }): ComponentRef<C>

So you would now do the following to add a dynamically loaded component to the DOM.

const module = await import('path/to/component/file');
const componentDef = module['MyComponent'];

const container: ViewContainerRef = /*retrieved using @ViewChild*/
container.createComponent(componentDef)

However, the docs for the new signature of ViewContainerRef.createcomponent show the 2nd optional argument is an object called options that has an optional property ngModuleRef:NgModuleRef<unknown>. For which the docs state:

ngModuleRef: an NgModuleRef of the component's NgModule, you should almost always provide this to ensure that all expected providers are available for the component instantiation.

Question

I've struggled to find any example usage of ViewContainerRef.createcomponent that actually includes a value for ngModuleRef and with the deprecation of ComponentFactoryResolver and ComponentFactory am wondering what the correct/official way to retrieve the module in which the component is declared?

Edit

Just to clarify, i'm interested in whether it's still possible to retrieve the module of a component that's been dynamically loaded using the new method in Angular v13, without explicitly knowing the type of that module?

Halpern answered 27/1, 2022 at 16:47 Comment(0)
G
7

In Angular 13+, the solution is to create without using the DEPRECATED compiler.

without using the factory, you have to use ViewContainerRef.createComponent.

Import injector in constructor constructor(private _injector: Injector) {} create module reference using NgModuleRef(ngModule,parentInjector)

const moduleRef: NgModuleRef<T> = createNgModuleRef(module, this._injector);
this.viewRef.createComponent(componentToOpen, {ngModuleRef: moduleRef});

You can also read about all deprecations and possible replacements in the Angular doc https://angular.io/guide/deprecations

Edit 19.04.2023:

Since createNgModuleRef is deprecated in Angular 13, you should use createNgModule instead:

const moduleRef: NgModuleRef<T> = createNgModule(module, this._injector);
this.viewRef.createComponent(componentToOpen, {ngModuleRef: moduleRef});
Gibbet answered 9/5, 2022 at 15:11 Comment(3)
Thanks. In your example code where does module come from?Halpern
for module, you can import a separate module.ts or you can create a dynamic module @NgModule({ imports: [], declarations: [], exports: [], }) class ComponentModule { }; const module: Type<ComponentModule> = (<any>ComponentModule)Gibbet
Thanks Again. Sorry I think i might need to edit my question as it's actually better described by the title. Previously you could use const cf: ComponentFactory = cfg.resolveComponentFactory(componentDef); const compModule: NgModuleRef = (cf as any).ngModule; to dynamically retrieve a components module. Therefore not having to explicitly know the module type. I'm wondering if there's a way to do this, as it appears that you now need to explicitly declare the module yourself.Halpern
H
0

As i'm using single component angular modules (SCAMs) i also have access to the components module when the component file is loaded.

I'm using a naming convention of MyComponent whose module is named MyComponentModule so am able to access both the component Class and components NgModule Class using the component name. The NgModule Class can then be passed to createNgModuleRef to create the instance of NgModuleRef required for the createComponent method.

import {createNgModuleRef} from '@angular/core';

const injector: Injector = /* injected */
const compName = 'MyComponent';
const module = await import('path/to/component/file');
const componentClass = module[compName];
const ngModuleRef = createNgModuleRef(module[`${compName}Module`], injector);

const container: ViewContainerRef = /*retrieved using @ViewChild*/
container.createComponent(componentClass, {injector:injector, ngModuleRef:ngModuleRef})

However, this has the drawback of having to have direct access to the components module type, which is not ideal.

Halpern answered 29/1, 2022 at 15:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.