What kind of components are dynamic components? Well, the ‘dynamic’ in dynamic components refers to the fact that we actually don’t define them at the build-time (but create them during runtime), and that they are not referenced in the templates. Even though they do not exist visually until a certain condition is met or an event is triggered, we need to declare them during build-time inside a module, because Angular needs a compilation context, and new components should always be associated with a module. This means that Angular knows about the component during build-time, but it only creates the component at runtime.
One way (although deprecated) was to use the ComponentFactoryResolver
.
ComponentFactoryResolver
Keep in mind that ComponentFactoryResolver
is deprecated since Angular 13, and the framework no longer needs Component factories. We can use Component classes directly.
The ComponentFactoryResolver is used to generate component factories that can be then used to create specific instances of components. We are using the resolver interface to first obtain the factory for the component, and then create them using the provided create()
method.
<ng-template #id></ng-template>
export class UserCardComponent implements OnInit, AfterViewInit { @ViewChild('id', { read: ViewContainerRef }) view: ViewContainerRef; constructor(private componentFactoryResolver: ComponentFactoryResolver) {} ngOnInit() {} ngAfterViewInit() { const componentFactory = this.componentFactoryResolver.resolveComponentFactory(IdComponent); let componentRef: ComponentRef<IdComponent> = this.view.createComponent(componentFactory); } }
If you open this code in any modern IDE you’ll notice that both the ComponentFactoryResolver
reference and createComponent()
method are struck out, indicating the deprecated state. The type ComponentRef
indicated that this component was dynamically created by a component factory. With this, we have access to the instance of the component and we can provide properties, or directly influence the lifecycle of the instance, including the destroy()
method.
If we run this, we would have something like this:
All that we can do now is provide the properties of the instance, like this:
ngAfterViewInit() { const componentFactory = this.componentFactoryResolver.resolveComponentFactory(IdComponent); let componentRef: ComponentRef<IdComponent> = this.view.createComponent(componentFactory); componentRef.instance.backgroundColor = 'blue'; componentRef.instance.foregroundColor = 'white'; componentRef.instance.fontWeight = 'bold'; }
Those last three lines directly set property values for the component instance.
As mentioned above, since Angular 13, we do not require resolving component factory, and we can dynamically create components with less code! We will show you how to do this right away!
Directly creating components
Instead of using the resolver, we can create component classes directly. And this can be as easy as just writing ViewContainerRef.createComponent
without a factory! The method does the same thing (instantiates the component and inserts it into the container), and has an optional set of params called options
, that configures properties such as the index in which to insert the component into the host, the injector used for the new component, list of projected nodes and a module reference that makes sure all the providers are available for the component. A more detailed explanation can be found on the official page for createComponent
.
This is as simple as writing the following:
const componentRef = this.view2.createComponent(IdComponent); componentRef.instance.backgroundColor = 'pink'; componentRef.instance.foregroundColor = 'black'; componentRef.instance.fontWeight = 'normal';
As you can see, we removed the need to use the factory resolver at all, and to dynamically create this component we used ViewContainerRef.createComponent()
directly. And, let us not forget to declare the ChangeDetectorRef
in order to get rid of the “expression changed after it has been checked” error.
Conclusion
Dynamically creating smart components is done using the createComponent()
method provided in the ViewContainerRef
namespace. But what is it useful for? Well, anything! And especially components that trigger programmatically, are needed to be absolutely positioned, or modals.
The StackBlitz for this project can be found below: