27

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

Elidor00
  • 1,271
  • 13
  • 27
xprojects
  • 271
  • 1
  • 3
  • 3

2 Answers2

45

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);

}
Elidor00
  • 1,271
  • 13
  • 27
  • Thanks for reply. i know that but here i don´t have any possibility to create to component inside the given el: Element... How to do this? – xprojects Feb 02 '22 at 04:33
  • Technically in the constructor `el: Element` should not be used. Try instead `constructor(private element: ElementRef` – Elidor00 Feb 02 '22 at 09:10
  • 1
    ok thanks. And how to use the ElementRef together with ViewContainerRef? There is no possibility to set the ElementRef at ViewContainerRef dynamically without using @ViewChild!? – xprojects Feb 02 '22 at 09:16
  • If inject `elementRef` in the constructor, you are using the reference to the element itself. Otherwise, as you rightly said, if you want to be dynamic over elements or directives, you can use `@ViewChild` or [Using directive instead of ViewChild query](https://medium.com/angular-in-depth/here-is-how-to-get-viewcontainerref-before-viewchild-query-is-evaluated-f649e51315fb) (This link is for an example of how to use it also because this would probably be a different question from the one you asked) – Elidor00 Feb 02 '22 at 09:38
  • Yes you right. So if i have the ElemenRef itself. But inside this ElementRef i want do add a new Component dynamically. The ElementRef is generated dynamically and a cannot use ViewChild here... – xprojects Feb 02 '22 at 09:53
  • Without having a code example with the reproducible problem, maybe on StackBlitz or similar, it is difficult to help you.. – Elidor00 Feb 02 '22 at 10:07
  • Hey. I tried using Angular-Elements and i think this is the right way… – xprojects Feb 02 '22 at 14:02
  • How do you use ViewContainerRef to create a component in a service? – claudekennilol May 02 '22 at 14:09
  • `viewContainerRef.createComponent(YourElement)` – Elidor00 May 03 '22 at 07:25
13

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],
})
J. Bosi
  • 69
  • 6
  • I got the following error on Angular 14: core.mjs:6485 ERROR TypeError: Cannot read properties of undefined (reading 'viewContainerRef') – C Alonso C Ortega Jul 24 '22 at 04:41
  • How to you get the @Output from the loaded dynamic component to the parent? – rpmansion Sep 08 '22 at 16:41
  • easier just to add @ViewChild('container1', { read: ViewContainerRef }) container1!: ViewContainerRef; then create your component with using. container1.createComponent – manit Oct 10 '22 at 05:28
  • 2
    But - YourDynamicChildComponent is fixed, this setup will only create a single type - i.e. YourDynamicChildComponent on the fly. What if you want to create Several different types? Based on enum / string / type? YourDynamicChild1, YourDynamicChild2, YourDynamicChild3 etc... – redevill Mar 22 '23 at 23:37