Skip to content

Dynamic components in Angular

Angular Dynamic Components - Cover

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.

ComponentFactoryResolver and createComponent() are deprecated

If we run this, we would have something like this:

Dynamic components with ComponentFactoryResolver
Dynamic components with ComponentFactoryResolver

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.

Dynamic components with instance inputs
Dynamic components with instance inputs

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';
Showing both ways of creating components dynamically

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: