imdadhusen
imdadhusen

Reputation: 2494

How to access child template in reusable component in Angular 8/9/10/11

I have created common component which will render data in table format.

data-table.component.ts (this is reusable component, can be used by any child component)

    @Component({
      selector: 'data-table',
      templateUrl: './data-table.component.html',
      styleUrls: ['./data-table.component.css']
    })
    export class DataTableComponent implements OnInit {
    constructor() { }
    
      ngOnInit(): void {
         //wanted to access child here(or inside any method in this class) to set the width of each <td> from child
      //wanted to access #rowTemplate children from child for all 3 <td> 
      }
setColumnWidth(width: string = "") {
    if (width != "") {
      return 'width:' + width;
    }
  }
    }

data-table.component.html

<table style="width:100%;">
      <thead>
        <tr>
          <th *ngFor="let c of columns" style="{{setColumnWidth(c.width)}}">
            {{c.fieldTitle}}
          </th>
        </tr>
      </thead>
       <tbody>
        <ng-container *ngTemplateOutlet="rowTemplate"></ng-container>
      </tbody>
    </table>

resource-updates.component.ts (this is child component)

    @Component({
      selector: 'resource-updates',
      templateUrl: './resource-updates.component.html',
      styleUrls: ['./resource-updates.component.css']
    })
    export class ResourceUpdatesComponent implements OnInit {
      columns: Column[] = [];
      rows: any[] = [];
     
      constructor() { }
    
      ngOnInit(): void {
        //Preparing list for columns
        this.columns.push(new Column({ fieldName: 'Id', fieldTitle: 'Id', isSortable: false, width: '5%' }));
        this.columns.push(new Column({ fieldName: 'Title', fieldTitle: 'Title', width: '60%' }));
        this.columns.push(new Column({ fieldName: 'Type', fieldTitle: 'Type', width: '35%' }));
      
       //Preparing list for rows
       this.rows = [
{"id":5,"title":"Air Gas Guideline","type":"PPT"},
{"id":6,"title":"Air Pollution User Reference","type":"Website"},
{"id":18,"title":"Avian Influenza (H7N9) User Reference","type":"Website"},
{"id":12,"title":"Avian Influenza (H7N9) for high risk","type":"PPT"},
{"id":11,"title":"Avian Influenza (H7N9) for normal","type":"PPT"}
];
      }
    }

resource-updates.component.html (wanted to set width of 3 below, in Parent (common component) from columns.width property)

  <ng-template #rowTemplate>
    <tr *ngFor="let r of rows">
      <td>{{r.id}}</td>
      <td>{{r.title}}</td>
      <td>{{r.type}}</td>
    </tr>
  </ng-template>
  <data-table [columns]="columns" [rowTemplate]="rowTemplate"></data-table>

Can any body help to access #rowTemplate children element for setting up width from columns[0].width and respectively...

Upvotes: 3

Views: 905

Answers (2)

Santi Barbat
Santi Barbat

Reputation: 2295

You need to define the @ContentChild and add @Input() annotation to the columns variable in your DataTableComponent (included the Column interface which wasn't in your question):

export interface Column {
  fieldName: string;
  fieldTitle: string;
  isSortable?: boolean;
  width?: string;
}

@Component({
  selector: 'app-data-table',
  templateUrl: './data-table.component.html',
  styleUrls: ['./data-table.component.css']
})
export class DataTableComponent implements OnInit {

  @Input() columns: Column[] = [];

  @ContentChild('rowTemplate') rowTemplate: TemplateRef<any>;

  constructor() { }

  ngOnInit(): void {
  }
}

The template of the DataTableComponent should look like the following:

<table style="width:100%;">
  <thead>
    <tr>
      <th *ngFor="let c of columns" [ngStyle]="{ width: c.width }">
        {{c.fieldTitle}}
      </th>
    </tr>
  </thead>
  <tbody>
    <ng-container *ngTemplateOutlet="rowTemplate"></ng-container>
  </tbody>
</table>

Finally the ResourceUpdatesComponent template:

<app-data-table [columns]="columns">
  <ng-template #rowTemplate>
    <tr *ngFor="let r of rows">
      <td>{{r.id}}</td>
      <td>{{r.title}}</td>
      <td>{{r.type}}</td>
    </tr>
  </ng-template>
</app-data-table>

Uploaded the working Angular project code to Github

There is a nice article explaining how @ContentChild works: Understanding ViewChildren, ContentChildren, and QueryList in Angular


Note that removed the setColumnWidth() function and made use of ngStyle, "that will be called every time the Angular change detection runs", see Why you should never use function calls in Angular template expressions

Upvotes: 3

Kevin Zhang
Kevin Zhang

Reputation: 1072

If I am not understanding wrong, you want to access to child component's ng-template from parent component? If it is, this is the case that I used before when create data-table controls.

  1. You can create a directive class with TemplateRef in it:
import { Directive, ContentChild, TemplateRef, Input } from "@angular/core";
@Directive({ selector: "np-column" })
export class NpColumnDirective {
      @Input() name: string;
      @ContentChild(TemplateRef) cellTemplate: TemplateRef<any>;
      constructor() { }
}
  1. Then in your parent compoent access it using:

@ContentChildren(NpColumnDirective) columnComponents: QueryList<NpColumnDirective>;

Here is the whole code I used for creating data-table, just for your reference: datatable demo

Upvotes: 1

Related Questions