How do I access the component instance from code?

611 Views Asked by At

I am using an ngComponentOutlet to dynamically display a caller-supplied component. My code knows absolutely nothing about the supplied component type. The template boils down to this:

<div [innerText]="options.title"></div>
<ng-container *ngComponentOutlet="options.componentType"></ng-container>
<button (click)="showInfo()">Info</button>

Speaking in a simplified manner, the caller for my UI passes a config object similar to the following:

export interface DisplayOptions<TComponent> {
  readonly componentType: Type<TComponent>;
  
  readonly title: string;
  
  readonly onClickInfo: (cmp: TComponent) => void;
}

The UI surrounding the component outlet contains a couple of buttons, such as an Info button shown above. When that Info button is clicked, the onClickInfo function should be invoked, which needs to receive a reference to the component.

Inside that showInfo() function, how do I get my hands on the reference to the component instance in the component outlet?

1

There are 1 best solutions below

7
On BEST ANSWER

You should be able to get a reference to this component using a @ViewChild.

  1. Make sure you have imported NgComponentOutlet.
  2. Import ViewChild
  3. Create a @ViewChild to get a reference to the component directive:
  • @ViewChild(NgComponentOutlet, { static: false }) ngComponentOutlet: NgComponentOutlet;
  1. Modify your show info:
  • showInfo() { if (this.ngComponentOutlet) { const componentInstance = this.ngComponentOutlet['_componentRef'].instance; this.options.onClickInfo(componentInstance); } }

A full example would look something like this:

import { Component, Type, ViewChild, NgComponentOutlet } from '@angular/core';

export interface DisplayOptions<TComponent> {
  readonly componentType: Type<TComponent>;
  readonly title: string;
  readonly onClickInfo: (cmp: TComponent) => void;
}

@Component({
  selector: 'app-dynamic',
  template: `
    <div [innerText]="options.title"></div>
    <ng-container *ngComponentOutlet="options.componentType"></ng-componentOutlet>
    <button (click)="showInfo()">Info</button>
  `
})
export class DynamicComponent<TComponent> {
  @ViewChild(NgComponentOutlet, { static: false }) ngComponentOutlet: NgComponentOutlet;
  options: DisplayOptions<TComponent>;

  showInfo() {
    if (this.ngComponentOutlet) {
      const componentInstance = this.ngComponentOutlet['_componentRef'].instance;
      this.options.onClickInfo(componentInstance);
    }
  }
}

You query the ComponentOutlet using the @ViewChild and store it in the ngComponentOutlet prop. showInfo() can then access the ['_componentRef'].instance prop of ngComponentOutlet.

Another way to do this would be to go with an @Input which is similar, but you'd have to update the HTML and pass in a primary key that you can use like an Id.