Mackelito
Mackelito

Reputation: 4421

Inject component on click in a *ngFor

I have followed the tutorial over at https://angular.io/docs/ts/latest/cookbook/dynamic-component-loader.html and it´s working just fine :)

Now I would like to do it a bit more advanced. http://plnkr.co/edit/D1q7Al60m4UK1weRlcmQ?p=preview

What I want is that clicking on a name should open up the details underneath on a separate row.

eg. clicking on "Luke Skywalker" or "C3PO" should create a row between the first and second row and show the details.

I have tried to dynamically add the "product-host" attribute but not working as the directive expects the attributes to exists.

<div *ngFor="let person of persons.results" fxFlex="50%" fxFlex.gt-sm="33%" person-host>

Upvotes: 2

Views: 839

Answers (1)

yurzui
yurzui

Reputation: 214057

I would use ViewChildren to achieve it. There are two possible ways:

1) Just pass index in your showPerson function

template

 <div *ngFor="let person of persons.results; let i = index" fxFlex="50%" fxFlex.gt-sm="33%" person-host>
   <md-card (click)="showPerson(person, i)">{{person.name}}</md-card>
 </div>

and then use it to determine desired place for info card

component

activatedViewContainerRef: ViewContainerRef;

@ViewChildren(PersonInjectorDirective) personHosts: QueryList<PersonInjectorDirective>;

loadPerson(person, index) {
  if(this.activatedViewContainerRef) {
    this.activatedViewContainerRef.clear();
  }
  let componentFactory = this._componentFactoryResolver.resolveComponentFactory(PersonComponent);
  this.activatedViewContainerRef = this.personHosts.find((item, i) => i === index).viewContainerRef;

  let componentRef = this.activatedViewContainerRef.createComponent(componentFactory);
  (<PersonComponent>componentRef.instance).person = person;
}

showPerson(person, index: number) {
  this.loadPerson(person, index % 2 ? index : index + 1);
}

ngOnDestroy() {
  if(this.activatedViewContainerRef) {
    this.activatedViewContainerRef.clear();
  }
}

Plunker Example

2) You can also build it without PersonInjectorDirective. In this case you need to declare template variable (#refs):

<div *ngFor="let person of persons.results; let i = index" fxFlex="50%" fxFlex.gt-sm="33%" #refs>
  <md-card (click)="showPerson(person, i)">{{person.name}}</md-card>
</div>

and change ViewChildren expression as follows:

@ViewChildren('refs', { read: ViewContainerRef}) personHosts: QueryList<ViewContainerRef>;

Plunker Example

Upvotes: 3

Related Questions