44

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

0

4 Answers 4

76

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

}
Sign up to request clarification or add additional context in comments.

9 Comments

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?
Technically in the constructor el: Element should not be used. Try instead constructor(private element: ElementRef<HTMLElement>
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!?
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 (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)
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...
|
17

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],
})

4 Comments

I got the following error on Angular 14: core.mjs:6485 ERROR TypeError: Cannot read properties of undefined (reading 'viewContainerRef')
How to you get the @Output from the loaded dynamic component to the parent?
easier just to add @ViewChild('container1', { read: ViewContainerRef }) container1!: ViewContainerRef; then create your component with using. container1.createComponent
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...
5

Here is what worked for me:

import {
  Component,
  ComponentRef,
  ElementRef,
  ViewChild,
  ViewContainerRef,
} from '@angular/core';
import {
  TestComponent
} from './TestComponent';

@Component({
  // ...
})
export class AppendComponent {
  constructor(private _ViewContainerRef: ViewContainerRef) {}

  @ViewChild('editor') editor!: ElementRef <HTMLElement> ;

  public appendComponent() {
    const component: ComponentRef <TestComponent> =
      this._ViewContainerRef.createComponent(TestComponent);

    const element: HTMLElement = component.location.nativeElement;
    element.contentEditable = 'false';

    this.editor.nativeElement.appendChild(element);
  }
}

Comments

1

In my application, I needed to place several Angular components outside of the Angular application, and without using the deprecated ComponentFactoryResolver, this could not be done.

Having spent a lot of time searching for solutions, I came across this post, on the basis of which I implemented my application, and I offer a shortened version of the solution for your consideration, maybe it will be useful to someone.

The disadvantage of this solution is that it is not possible to create components dynamically, because components must be created when the application is compiled. Maybe someone can improve this version and also implement dynamic creation of components.

sshot1.png

sshot2.png

sshot3.png

sshot4.png

1 Comment

Sure, since Angular 14.1, we have the createComponent function available which creates a component without attaching it to the DOM. See here: angular.io/api/core/createComponent

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.