How Angular Resolves Dynamic Components

What really happens when we use the resolveComponentFactory method in Angular? let’s peek under the hood.

My Photo courtesy of the Author

Introduction

Dynamic components are one of the core concepts introduced in Angular, they enable developers to dynamically render a component’s view during runtime. This article explains how the ComponentFactoryResolver class resolves a ComponentFactory .

Why should we know this? Knowing how Angular works its nuts and bolts, will go a long way to understand it better and also you’ll be better off when building applications and dealing with bug-related issues.

Tip: Share and reuse Angular Components

Use Bit to encapsulate Angular components with all their dependencies and setup. Share them in Bit’s cloud, collaborate with your team and use them anywhere.

Shared components in Bit’s cloud

Angular Components and Factories

On execution, Angular isn’t made up of Components. Everything is compiled down to JavaScript before execution. Components, Modules, Directives are all turned into factories. In other words, Angular is made up of factories (ComponentFactory, NgModuleFactory).

So before begin executed, this:

@Component({

selector: 'app-root',

template: `

<p *ngFor='let fruit of fruits'>{{fruit}}</p>

`,

styles: []

})

export class AppComponent {

title = 'app';

fruits = ['orange','apple']

constructor(){}



changeTitle() {

this.title = 'Angular app'

}

}

Would be compiled to this:

function View_AppComponent_0(_l) {

return i0.ɵvid(0, [

(_l()(), i0.ɵted(-1, null, ["

"])),

(_l()(), i0.ɵeld(1, 0, null, null, 4, "div", [], null, null, null, null, null)),

(_l()(), i0.ɵted(-1, null, ["

"])),

(_l()(), i0.ɵeld(3, 0, null, null, 1, "p", [], null, null, null, null, null)),

(_l()(), i0.ɵted(4, null, ["

", " works!!

"])),

(_l()(), i0.ɵted(-1, null, ["

"])),

(_l()(), i0.ɵted(-1, null, ["

"]))

], null, function(_ck, _v) {

var _co = _v.component;

var currVal_0 = _co.title;

_ck(_v, 4, 0, currVal_0);

});

}

exports.View_AppComponent_0 = View_AppComponent_0;



function View_AppComponent_Host_0(_l) {

return i0.ɵvid(0, [

(_l()(), i0.ɵeld(0, 0, null, null, 1, "app-root", [], null, null, null, View_AppComponent_0, RenderType_AppComponent)),

i0.ɵdid(1, 49152, null, 0, i2.AppComponent, [], null, null)

], null, null);

}

exports.View_AppComponent_Host_0 = View_AppComponent_Host_0;



var AppComponentNgFactory = i0.ɵccf("app-root", i2.AppComponent, View_AppComponent_Host_0, {}, {}, []);

exports.AppComponentNgFactory = AppComponentNgFactory;

In this article we will look in-depth how Angular resolves a dynamic component’s factory using resolveComponentFactory method:

const factory = r.resolveComponentFactory(AComponent);

factory.create(injector);

Example

Let’s say we have a ChildComponent :

@Component({

selector: 'child',

template: `

<h1>Child Component</h1>

`,

})

export class ChildComponent {

}

And a ParentComponent that dynamically appends the ChildComponent's view onto its own:

@Component({

selector: 'app-root',

template: `

<template #parent></template>

<button (click)="createChild()">Create Child</button>

`,

})

export class ParentComponent {

@ViewChild('parent', { read: ViewContainerRef }) container;



constructor(private resolver: ComponentFactoryResolver) {}



createChild() {

this.container.clear();

const factory: ComponentFactory = this.resolver.resolveComponentFactory(ChildComponent);

this.componentRef: ComponentRef = this.container.createComponent(factory);

}

}

Great! The template element is where the ChildComponent will be appended.

The most important things we have to note here is the injection of the ComponentFactoryResolver and the resolution of the ChildComponent in the createChild method. When the Create Child button is clicked, the createChild method is run which first clears the template element, then get the Component factory of the ChildComponent and goes forward to create the ChildComponent 's view on the DOM.

The question here is: What is ComponentFactoryResolver? and how does its method resolveComponentFactory get the factory of a component?

What is ComponentFactoryResolver ?

ComponentFactoryResolver is a class in Angular that stores the factories of components in a key-value store.

--------------- key | value ----------------

[TodoComponent => TodoComponentNgFatory]

[AboutComponent => AboutComponentNgFactory]

It takes the array of ComponentFactory type, a parent ComponentFactoryResolver and a NgModuleRef as arguments on instantiation.

export class CodegenComponentFactoryResolver implements ComponentFactoryResolver {



constructor(

factories: ComponentFactory<any>[], private _parent: ComponentFactoryResolver,

private _ngModule: NgModuleRef<any>) {...}

}

During the construction of its object, its loops through the factories array and stores each ComponentFactory in a Map with the componentType as its key and the factory as the value:

private _factories = new Map<any, ComponentFactory<any>>();



constructor(

factories: ComponentFactory<any>[], private _parent: ComponentFactoryResolver,

private _ngModule: NgModuleRef<any>) {

for (let i = 0; i < factories.length; i++) {

const factory = factories[i];

this._factories.set(factory.componentType, factory);

}

}

This is more like an object. It uses key-value mechanism to store and retrieve values:

var store = {

//key : value

one: 1,

two: 2

} // To get the value `1`, we reference with its key `one`

console.log(store.one) // 1 // Value can be stored with its key `three => 3` store['three'] = 3

console.log(store.three) // 3

How does resolveComponentFactory get the factory of a component?

Simple, as it stores it in a Map with a key-value pair. All it has to do to get the factory of a component is to reference it by its key.

resolveComponentFactory<T>(component: {new (...args: any[]): T}): ComponentFactory<T> {

let factory = this._factories.get(component);

if (!factory && this._parent) {

factory = this._parent.resolveComponentFactory(component);

}

if (!factory) {

throw noComponentFactoryError(component);

}

return new ComponentFactoryBoundToModule(factory, this._ngModule);

}

The get method is used in a Map to get the value by its key. So, here the get method is used to retrieve the ComponentFactory by its key ( component ).

The next question is how does ComponentFactoryResolver receive the ComponentFactory array?

Earlier at our article introduction, we saw that components are compiled down to JS code called factories before being executed. Components are compiled to generate ComponentFactory on execution, and Modules generate NgModuleFactory.

createNgModuleFactory function is used to generate NgModuleFactory.

The AppModule of our app:

@NgModule({

declarations: [

AppComponent,

ChildComponent

],

imports: [

BrowserModule

],

providers: [],

bootstrap: [AppComponent]

})

export class AppModule { }

Its factory will look like this:

var AppModuleNgFactory = i0.ɵcmf(i1.AppModule, [i2.AppComponent], function(_l) {

return i0.ɵmod([i0.ɵmpd(512, i0.ComponentFactoryResolver, i0.ɵCodegenComponentFactoryResolver, [

[8, [i3.AppComponentNgFactory]],

[3, i0.ComponentFactoryResolver], i0.NgModuleRef

]), i0.ɵmpd(5120, i0.LOCALE_ID, i0.ɵm, [

[3, i0.LOCALE_ID]

]), ...

i0.ɵmpd(131584, i0.ApplicationRef, i0.ApplicationRef, [i0.NgZone, i0.ɵConsole, i0.Injector, i0.ErrorHandler, i0.ComponentFactoryResolver, i0.ApplicationInitStatus]), i0.ɵmpd(512, i0.ApplicationModule, i0.ApplicationModule, [i0.ApplicationRef]), i0.ɵmpd(512, i5.BrowserModule, i5.BrowserModule, [

[3, i5.BrowserModule]

]), i0.ɵmpd(512, i6.ɵba, i6.ɵba, []), i0.ɵmpd(512, i6.FormsModule, i6.FormsModule, []), i0.ɵmpd(512, i1.AppModule, i1.AppModule, [])]);

});

Notice the obscure method names starting theta ɵ . This is employed by Angular to reduce bundle size on minification. These method reference the actual method implementation, ɵcmf calls createNgModuleFactory, ɵmpd => moduleProviderDef, ɵmod => moduleDef.

We see that createNgModuleFactory takes a Module class, bootstrap components, and a module definition factory as arguments.

export function createNgModuleFactory(

ngModuleType: Type<any>, bootstrapComponents: Type<any>[],

defFactory: NgModuleDefinitionFactory): NgModuleFactory<any> {

return new NgModuleFactory_(ngModuleType, bootstrapComponents, defFactory);

}

We will be more interested in the module definition factory. NgModuleFactroyDefinition provides the context used for component resolution and dependency injection.

Looking into it, we see that it calls moduleDef function which is passed an array of moduleProviderDef calls.

moduleProviderDef is a function that defines how a class will be stored and retrieved by an Injector.

export function moduleProvideDef(

flags: NodeFlags, token: any, value: any,

deps: ([DepFlags, any] | any)[]): NgModuleProviderDef {

...

return {

// will bet set by the module definition

index: -1,

deps: depDefs, flags, token, value

};

}

Here the flags parameter, denotes how the value of a class/provider should be resolved or instantiated:

export const enum NodeFlags {

...

TypeValueProvider = 1 << 8,

TypeClassProvider = 1 << 9,

TypeFactoryProvider = 1 << 10,

TypeUseExistingProvider = 1 << 11

...

}

Next, the token: any parameter is the key by which the value of the class/provider is retrieved by the Injector. value is the actual value of the token . deps: ([DepFlags, any] | any)[] is the dependencies used to construct the instance of the class.

Now, moduleProviderDef parses all this into an object and returns it. moduleDef receives an array of it and parses it in an array of providers .

export function moduleDef(providers: NgModuleProviderDef[]): NgModuleDefinition {

...

return {

// Will be filled later...

factory: null,

providersByKey,

providers

};

}

When module factory is created through createNgModuleFactory function call, a series of calls are made which eventually lead to the resolution of the providers:

export function initNgModule(data: NgModuleData) {

const def = data._def;

const providers = data._providers = new Array(def.providers.length);

for (let i = 0; i < def.providers.length; i++) {

const provDef = def.providers[i];

if (!(provDef.flags & NodeFlags.LazyProvider)) {

providers[i] = _createProviderInstance(data, provDef);

}

}

}

Here the providers array generated by moduleDef function is looped through and instance of each provider is created and stored in _providers property of the NgModuleDef_ :

function _createProviderInstance(ngModule: NgModuleData, providerDef: NgModuleProviderDef): any {

let injectable: any;

switch (providerDef.flags & NodeFlags.Types) {

case NodeFlags.TypeClassProvider:

injectable = _createClass(ngModule, providerDef.value, providerDef.deps);

break;

case NodeFlags.TypeFactoryProvider:

injectable = _callFactory(ngModule, providerDef.value, providerDef.deps);

break;

case NodeFlags.TypeUseExistingProvider:

injectable = resolveNgModuleDep(ngModule, providerDef.deps[0]);

break;

case NodeFlags.TypeValueProvider:

injectable = providerDef.value;

break;

}

return injectable === undefined ? UNDEFINED_VALUE : injectable;

}

We went through all of this to deduce how ComponentFactoryResolver gets its ComponentFactory array. Next, let’s look at our module factory definition.

return i0.ɵmod([i0.ɵmpd(512, i0.ComponentFactoryResolver, i0.ɵCodegenComponentFactoryResolver, [

[8, [i3.AppComponentNgFactory]],

[3, i0.ComponentFactoryResolver], i0.NgModuleRef

])

Looking at how ComponenetFactoryResolver is configured, we see that 512 , which denotes TypeClassProvider in NodeFlags object, is passed in. This tells Angular we are configuring the provider from a class.

Next, i0.ComponentFactoryResolver is passed as a token, and i0.ɵCodegenComponentFactoryResolver as the value. The last param is the dependencies of ComponentFactoryResolver.

Remember, when we looked into ComponentFactoryResolver definition earlier, we saw it takes factories: ComponentFactory , private _parent: ComponentFactoryResolver , private _ngModule: NgModuleRef as parameters in its constructor.

So, here ComponentFactoryResolver instance will be instantiated like this:

const resolver = new ComponentFactoryResolver([i3.AppComponentNgFactory], i3.ComponentFactoryResolver, i0.NgModuleRef)

As you see, an array of ComponentFactory is passed to it. So, during the construction of its object, it loops through the array and stores the key-value pair of each ComponentFactory in the _factories Map property.

So when we do this:

...

createChild() {

...

const factory: ComponentFactory = this.resolver.resolveComponentFactory(ChildComponent);

...

}

...

ComponentFactoryResolver loops through _factories Map and retrieves the ComponentFactory from the key ChildComponent supplied.

If we run our app like this and click on the Create Child button, noComponentFactoryError error will be thrown.

Why? Because the ChildComponent was not passed to ComponentFactoryResolver in the ComponentFactory array param, so it wasn't present in the _factories key-value store.

This is the reason tutorials dealing with Dynamic Angular Components always refer to add all your dynamically created components in the entryComponents property of your root NgModule (AppModule).

@NgModule({

declarations: [

AppComponent,

ChildComponent

],

imports: [

BrowserModule,

FormsModule

],

providers: [],

entryComponents:[AppComponent,ChildComponent],

bootstrap: [AppComponent]

})

export class AppModule { }

So, Angular Compiler “know” they are to be added to ComponentResolverFactory dependencies during compilation.

var AppModuleNgFactory = i0.ɵcmf(i1.AppModule, [i2.AppComponent], function(_l) {

return i0.ɵmod([i0.ɵmpd(512, i0.ComponentFactoryResolver, i0.ɵCodegenComponentFactoryResolver, [

[8, [i3.AppComponentNgFactory, i3.ChildComponentNgFactory]],

[3, i0.ComponentFactoryResolver], i0.NgModuleRef

]), ... ]);

});

Now, when ComponentFactoryResolver dependency is configured, the ChildComponentFactory will be present:

const resolver = new ComponentFactoryResolver([i3.AppComponentNgFactory,i3.ChildComponentNgFactory], i3.ComponentFactoryResolver, i0.NgModuleRef)

Conclusion

We have seen how Angular resolves dynamic components. The moduleProviderDef function is used to configure a provider for the Angular dependency injection framework.

This is where ComponentFactory -s of dynamic components are passed to ComponentFactoryResolver class for resolution of a component's factory during runtime.

That’s it, now we know what happens under the hood when we use the resolveComponentFactory method.

Thanks!!!