5

How I can use component as input data for another component in Angular?

For example:

I want bulid table component AppTableComponent:

<app-table [dataSource]="dataSource" [columns]="columns">
  <ng-container tableColumnDef="actions">
    <a routerLink="/model/edit/{{item.id}}" routerLinkActive="active">Edit</a>
    <a routerLink="/model/delete/{{item.id}}" routerLinkActive="active">Delete</a>
  </ng-container>
  <ng-container tableColumnDef="isActive">
    <div [ngClass]="{cercl:true, 'is-active':item.is_active}">&nbsp;</div>
  </ng-container>
</app-table>

dataSource is data array sumthing like Model[] or Person[] or Car[]. columns is a string array like ['id', 'isActive', 'name', 'actions']. It should containt dataSource rows attibutes names or addition columns names.

I know how I can use ng-content but it's not samular case. Difference is I should use parts of content in sereral places. Perhaps I should use ng-contet, but I don’t know something.

I'm sure my goal is posible becouse Angular material table work like this:

<mat-table #table [dataSource]="dataSource">
    <ng-container matColumnDef="position"></ng-container>
    <ng-container matColumnDef="weight"></ng-container>
</mat-table>

Please don't propose me use Angular material table component. I don't need table. I just want to learn something new.

I'll be thenkful any information or article about topic!

Lakremon
  • 787
  • 1
  • 8
  • 26
  • are you familiar with [Angular's Input() and Output()](https://angular.io/guide/component-interaction) ? – Stavm Nov 04 '18 at 06:41

2 Answers2

4

If you want to manage the template on consumer part then you have to use Angular embedded view(ng-template). And that is what material uses in its table implementation.

<table mat-table [dataSource]="dataSource">
  <ng-container matColumnDef="position">
    <th mat-header-cell *matHeaderCellDef> No. </th>
    <td mat-cell *matCellDef="let element"> {{element.position}} </td>
  </ng-container>

You could say that there is no any embedded view but lets see on expanded version of the template above:

<table mat-table [dataSource]="dataSource">
  <ng-container matColumnDef="position">
    <ng-template matHeaderCellDef>
      <th mat-header-cell> No. </th>
    </ng-template>
    <ng-template matCellDef let-element="$implicit">
      <td mat-cell> {{element.position}} </td>
    </ng-template>
  </ng-container>

we can notice <ng-template matHeaderCellDef> here which can be obtained by using ContentChild.

Angular material team creates dedicated directives for such templates https://github.com/angular/material2/blob/f2c7205d6608d36a2016d90090be2a78d4f3233e/src/lib/table/cell.ts#L32 which keep references to embedded templates https://github.com/angular/material2/blob/676ce3b285718d2ee19ad6ae5702917566167641/src/cdk/table/cell.ts#L34

Material table component has template like:

<ng-container headerRowOutlet></ng-container>
<ng-container rowOutlet></ng-container>
<ng-container footerRowOutlet></ng-container>

There are also directives-helpers like:

@Directive({selector: '[headerRowOutlet]'})
export class HeaderRowOutlet implements RowOutlet {
  constructor(public viewContainer: ViewContainerRef,
              public elementRef: ElementRef) { }
}

So that we can use low level api to create elements based on embedded template, for example ViewContainerRef.createEmbeddedView(templateRef) but simple implementation can be found here:

yurzui
  • 205,937
  • 32
  • 433
  • 399
2

You can use @ContentChild('tableHeader') header: TemplateRef<any>; You can use ContentChild to get the first element or the directive matching the selector from the content DOM.

If the content DOM changes, and a new child matches the selector, the property will be updated.

Your child component.html reads as

 <ng-template [ngTemplateOutlet]="header"></ng-template>
 <ng-template [ngTemplateOutlet]="body"></ng-template>

Where [ngTemplateOutlet] will read the property value and paste the content in the specified template

Finally when you refer the content from the parent component it will get placed with the template mentioned above

Method to pass the content from parent to child

<compoent-selector>
     <ng-template #tableHeader></ng-template>
     <ng-template #body></ng-template>
</component-selector>

Any content inside this <ng-template> will be placed inside the child component

In child component you can add as many templates you can where all the templates will be placed with the content passed form the parent component - This is another way to pass content from parent to child - hope this helps - Thanks Happy coding !!

click here for Reference

Al Fahad
  • 2,378
  • 5
  • 28
  • 37
Rahul
  • 2,040
  • 2
  • 11
  • 29