Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular: add a component inside an innerHTML

I would like to inject a component through an innerHTML.

  <article class="card-body">
    <div class="body-container" [innerHTML]="bodyCard.content"></div>
  </article>
  @Input() bodyCard: BodyCard = {
    content: `
    <p class="body-content">
     some text
     // then the component
    <ftn-tooltip></ftn-tooltip>
    </p>   

I have no error but the component does not display. I checked with ComponentFactoryResolver but it seems to work only with ng-template and not with a HTML tag such as a DIV.

Any reccomendation please ?

like image 298
Younes Avatar asked Jun 12 '26 07:06

Younes


1 Answers

Generally you can inject a component using componentFactoryResolver

A directive like

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

Allow get the "ViewContainerRef" of a ng-template or div. adding in a html tag

<ng-template adHost></ng-template>

Then we can get the "ng-template" and his ViewContainerRef using ViewChild

@ViewChild(AdDirective, {static: true}) adHost!: AdDirective;

And in any moment use like

//get the component -see that we put the Class of component
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(MyComponent);

//see we get the viewContainerRef of the "directive"
const viewContainerRef = this.adHost.viewContainerRef;
viewContainerRef.clear();

const componentRef = viewContainerRef.createComponent<MyComponent>(componentFactory);

Some authores, like Dave Rivera prefer to have a service. It's the same idea but its a bit transparent because all the responsability of create the component is translatate to the service

export class LoaderService {  
  viewContainerRef:ViewContainerRef;
  constructor(private factoryResolver:ComponentFactoryResolver) {
  } 
  
  setViewContainerRef(viewContainerRef) {
    this.viewContainerRef = viewContainerRef
  }  
  
  addHelloComponent() {
    const factory = this.factoryResolver
                        .resolveComponentFactory(HelloComponent)

    this.viewContainerRef.clear();
    
    const componentRef = this.viewContainerRef.createComponent<HelloComponent>(factory);
    return componentRef.instance;
  }}

Now, we can inject the service, call the function AddHelloComponent passing a ViewContainerRef in any moment if we has a constructor

constructor(private loaderService:LoaderService){}

//and in any moment
this.loaderService.addHelloComponent(this.adHost.viewContainerRef);

we can join the two concepts and create a directive like

@Directive({
  selector: '[adHello]',
})
export class AdHelloDirective implements OnInit {
  @Input('adHello') name
  constructor(public viewContainerRef: ViewContainerRef,private factoryResolver:ComponentFactoryResolver) { }
  ngOnInit(){
    const factory = this.factoryResolver
    .resolveComponentFactory(HelloComponent)

this.viewContainerRef.clear();

const componentRef = this.viewContainerRef.createComponent<HelloComponent>(factory);
 componentRef.instance.name=this.name;

  }
}

that it's used like

<ng-template adHello="new from adhelo"></ng-template>

See stackblitz

like image 149
Eliseo Avatar answered Jun 14 '26 02:06

Eliseo



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!