'Replace deprecated Angular ComponentFactoryResolver, ComponentFactory

I create dynamic components in an HTMLElement with the following code:

import {ApplicationRef, ComponentFactory, ComponentFactoryResolver, ComponentRef, Injector} from '@angular/core';
import {DynamicTableComponent} from '../table/dynamic-table/dynamic-table.component';

export class Table {
    
    private readonly el: Element;
    private compRef!: ComponentRef<DynamicTableComponent>;

    constructor(el: Element, private resolver: ComponentFactoryResolver, private injector: Injector, private appRef: ApplicationRef) {        
        this.el = el;
    }

    public destruct(): void {
        if (this.compRef !== null && this.compRef !== undefined) {
            this.appRef.detachView(this.compRef.hostView);
            // is done by this.compRef.destroy();
            // this.compRef.instance.ngOnDestroy();
            this.compRef.destroy();
        }
    }

    public addTable(el: Element): void {
        const compFactory: ComponentFactory<any> = this.resolver.resolveComponentFactory(DynamicTableComponent);
        this.compRef = compFactory.create(this.injector, undefined, el);
        this.appRef.attachView(this.compRef.hostView);
    }
}

The component is dynamically loaded into an HTML element and added via attachView. The destruct() method then removes the component cleanly. It´s working fine but since Angular 13 it´s deprecated. So I don´t have an ViewContainerRef and I don´t really know how to do it right in > Angular 13!?

Do you have any advice for me?

Thanks and greetings



Solution 1:[1]

In Angular 13 the new API removes the need for ComponentFactoryResolver being injected into the constructor, like you did in your code.

Now to dynamically create a component you have to use ViewContainerRef.createComponent without using the factory.

So, instead of using

const compFactory: ComponentFactory<any> = this.resolver.resolveComponentFactory(DynamicTableComponent);

you can do:

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

/**
your code logic
*/

constructor(private viewContainerRef: ViewContainerRef)

public addTable(): void {
   const component = this.viewContainerRef.createComponent(YourElement);

}

Solution 2:[2]

After spending a few hours I have reached the correct solution for Angular 13 Ivy with the new Update of Component API:

First you need to create a directive, the best would be in a subfolder called 'directives', you can do it manually or use the command:

ng generate directive directives/DynamicChildLoader

This will generate the file:

dynamic-child-loader.directive.ts

There your code will need to be something like:

import { Directive, ViewContainerRef } from '@angular/core';

@Directive({
  selector: '[dynamicChildLoader]',
})
export class DynamicChildLoaderDirective {
  constructor(public viewContainerRef: ViewContainerRef) {}
}

Now in your component go to the .HTML and you need to include this:

<ng-template dynamicChildLoader=""></ng-template>

And in your .TS:

@ViewChild(DynamicChildLoaderDirective, { static: true })
  dynamicChild!: DynamicChildLoaderDirective;

  ngOnInit(): void {
    this.loadDynamicComponent();
  }

 private loadDynamicComponent() {
    this.dynamicChild.viewContainerRef.createComponent(YourDynamicChildComponent);
  }

Don't forget that in your app.module.ts or the module you are using to include in the Declarations part your DynamicChildLoaderDirective

@NgModule({
  declarations: [MyExampleComponent, DynamicChildLoaderDirective],
  imports: [CommonModule, ComponentsModule],
})

Sources

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

Source: Stack Overflow

Solution Source
Solution 1 Elidor00
Solution 2 DylanM