Home > Blockchain >  Pass context to angular component selector using ng-content
Pass context to angular component selector using ng-content

Time:12-31

I have angular components, where I'm projecting one component into another via content. For example:

app-component-a-controls is projected into app-component-a. app-component-a draws several app-component-a-controls via *ngFor.

app-component-a HTML:

<app-component-a>
   <app-component-a-controls>
     <button (click)="log(record)">Click Me!</button>
   </app-component-a-controls>
</app-component-a>

app-component-a-controls HTML:

<... *ngFor="...">
   <ng-content select="app-component-a-controls"></ng-content>
</...>

I need somehow to access items outside, for example when I click button Click Me!, I need to get ngFor corresponding item as parameter called record. Something like the following:

<app-component-a>
   <app-component-a-controls let-record>
      <button (click)="log(record)">Click Me!</button>
   </app-component-a-controls>
</app-component-a>

Any ideas, how can I achieve it?

CodePudding user response:

You can pass your button through an @Input() of <app-component-a-controls> as a TemplateRef.

So you app component template would look something like this....

app.component.html

<app-component-a>
  <app-component-a-controls [buttonTemplate]="buttonTemplateExample"></app-component-a-controls>
  <ng-template let-record="record" #buttonTemplateExample>
    <button (click)="log(record)">Click Me!</button>
  </ng-template>
</app-component-a>

Notice the let-record="record" on the ng-template which we declare as a template variable that will be resolved when we render the template in the app-component-a-controls component.

Then in your app-component-a-controls template render the button template using ng-container with [ngTemplateOutlet] and then use [ngTemplateOutletContext] to pass a "record" value to our template...

app-component-a-controls.component.html

<div *ngFor="let control of controls">
  {{ control.label }}
  <ng-container 
    [ngTemplateOutlet]="buttonTemplate"
    [ngTemplateOutletContext]="{ record: control.record }">
  </ng-container>
</div>

app-component-a-controls.component.ts

import { Component, Input, OnInit, TemplateRef } from '@angular/core';

@Component({
  selector: 'app-component-a-controls',
  templateUrl: './component-a-controls.component.html',
  styleUrls: ['./component-a-controls.component.css'],
})
export class ComponentAControlsComponent implements OnInit {
  @Input()
  public buttonTemplate: TemplateRef<any>;

  public controls: any[] = [
    {
      label: 'ControlA',
      record: 'This is the value for Control A',
    },
    {
      label: 'ControlB',
      record: 'This is the value for Control B',
    },
  ];

  constructor() {}

  ngOnInit() {}
}

I put together a working stackblitz of this approach...

https://stackblitz.com/edit/angular-ivy-e3wjne

  • Related