How to get the Component instance of from ViewContainerRef.get() for components created dynamically

7.9k views Asked by At

Summary of what I'm trying to accomplish

  • dynamically add components to a ViewContainerRef (done)
  • initialized these Dynamic components with properties (done)
  • get access to the dynamically created Component instances so that I may make decisions based what is.

the problem

  • when adding components dynamically, they are added to a ViewContainerRef
  • ViewContainerRef offers methods, such as get(index) that return ViewRef.
  • ViewRef doesn't appear to have any relationship to the Component instance, making it a challenge to obtain the required data

Here is a Stackblitz Link with working code show below (for dynamically creating the components)

the appComponent starts off by creating a few component using ComponentFactoryResolver, and adding them to the ViewChild defined in the template. each DynamicComponent is initialized with an id property value that we are trying to reference after creation

@Component({
  selector: "my-app",
  template: `
    <h3>Retrieving Component Reference for Dyamic Compnents</h3>
    <button (click)="getCompRef()">Get References</button>
    <div>
      <ng-container #childCont></ng-container>
    </div>
    <div>
      <small>List out the Ids from the dynamic components</small> <br />
      {{ createdItemIds | json }}
    </div>
  `,
  styleUrls: ["./app.component.css"]
})
export class AppComponent implements AfterViewInit {
  @ViewChild("childCont", { read: ViewContainerRef })
  childCont: ViewContainerRef;

  createdItemIds: string[] = [];

  itemLimit = 5;

  constructor(
    private fr: ComponentFactoryResolver,
    private cdr: ChangeDetectorRef
  ) {}

  ngAfterViewInit(): void {
    for (let i = 0; i < this.itemLimit; i++) {
      const factory = this.fr.resolveComponentFactory(DynamicComponent);
      const compRef = this.childCont.createComponent(factory, i);
      // Set the id of the instance so that we can use it later
      compRef.instance.id = i + 1;
      this.cdr.detectChanges();
    }
  }
  ...
}

the DynamicComponent being added is fairly simple. for simplifications purposes, it only contains a single id property that we are trying to get at

@Component({
  selector: "dynamic-component",
  template: `
    <div>Dynamic Component: {{ id }}</div>
  `,
  styles: [``]
  ]
})
export class DynamicComponent {
  id: number;
} 

Everything is fine so far.

  • The components are dynamically created
  • the component instances are initialized with the ID, which we can see by the fat that it is displayed in the UI

the issue comes with trying to retrieve the ID property from the DynamicallyCreated components.

In the AppComponent, when the user clicks the button, the getCompRef() method gets called and loops through each child of the childCont (ViewContainerRef)

getCompRef(): void {
  for (let i = 0; i < this.itemLimit; i++) {
    const viewRef = this.childCont.get(i);
    // How do I get at the instance of the view  in order to obtain id?
    // the view Ref doesn't provide access to the instance
    // console.log(viewRef);
  }
}

however the ViewRef returned from ViewContainerRef.get() is an sub class of ChangeDetectoreRef and doesn't hold any reference to the instance in question.

In doing research on this issue, it tried going down the path of using ViewChildren to get the list components being created, but this didn't work because of issues such as

  • https://github.com/angular/angular/issues/8785
  • or examples assume the directive used in the ViewChildren selector is for a component that has been predefined in a template
  • I see a lot of question in reference to some folks looking to get the ViewRef when they have the Component.instance, but that isn't helpful in this situation.

Ultimately my question is,

  • is there a simple way to get at the Component instance from a ViewRef that I'm missing

Any help is appreciated.

thank you.

1

There are 1 answers

0
Jonathas Sucupira On

I could only do this by keeping track of the instances of these components in a different array.

@Component({
  selector: "my-app",
  template: `
    <h3>Retrieving Component Reference for Dyamic Compnents</h3>
    <button (click)="getCompRef()">Get References</button>
    <div>
      <ng-container #childCont></ng-container>
    </div>
    <div>
      <small>List out the Ids from the dynamic components</small> <br />
      {{ createdItemIds | json }}
    </div>
  `,
  styleUrls: ["./app.component.css"]
})
export class AppComponent implements AfterViewInit {
  @ViewChild("childCont", { read: ViewContainerRef })
  childCont: ViewContainerRef;

  createdItemIds: string[] = [];

  itemLimit = 5;
  private dynamicComponentsArray: ComponentRef<DynamicComponent>[] = [];

  constructor(
    private fr: ComponentFactoryResolver,
    private cdr: ChangeDetectorRef
  ) {}

  ngAfterViewInit(): void {
    for (let i = 0; i < this.itemLimit; i++) {
      const factory = this.fr.resolveComponentFactory(DynamicComponent);
      const compRef = this.childCont.createComponent(factory, i);
      // Set the id of the instance so that we can use it later
      compRef.instance.id = i + 1;
      this.dynamicComponentsArray.push(compRef);
      this.cdr.detectChanges();
    }
  }
  
  getCompRef(): void {
      for (let i = 0; i < this.itemLimit; i++) {
        const viewRef = this.childCont.get(i);
        if (this.dynamicComponentsArray.length > 0) {
            for (let i = 0; i < this.dynamicComponentsArray.length; i++) {
                const componentRef = this.dynamicComponentsArray[i];
                const component = (componentRef) as ComponentRef<DynamicComponent>;
                let value = component.instance.id;
            }
        }
      }
    }
}