angularprimengprimeng-table

How to forward ngTemplate to wrapped PrimeNg table


For an Angular application I want to create a table component that wraps a PrimeNG table. For this table component it should be possible to define a body template. Therefore I need to somehow forward the body template to the wrapped PrimeNG table, but I cannot figure out how to achieve that.

Following is a simplified version of my table component that wraps the PrimeNG table:

import { AfterContentInit, Component, ContentChildren, Input, QueryList, TemplateRef } from '@angular/core';
import { PrimeTemplate } from "primeng/api";

@Component({
    selector: 'app-my-table',
    template: `
        <p-table
            *ngIf="bodyTemplate"
            [value]="value"
        >
            <ng-template pTemplate="caption">
                {{title}}
            </ng-template>

            <ng-template pTemplate="header">
                <tr>
                    <th *ngFor="let h of headers">{{h}}</th>
                </tr>
            </ng-template>

            <!-- here should bodyTemplate go, but how? -->
            <ng-template pTemplate="body" [ngTemplateOutlet]="bodyTemplate"></ng-template>

        </p-table>
    `
})
export class MyTableComponent implements AfterContentInit {

    @ContentChildren(PrimeTemplate) templates!: QueryList<PrimeTemplate>;

    @Input() title: string = '';
    @Input() headers: string[] = [];
    @Input() value: any[] = [];

    bodyTemplate: TemplateRef<any> | undefined;

    ngAfterContentInit() {
        this.templates.forEach((item) => {
            switch (item.getType()) {
                case 'body':
                    this.bodyTemplate = item.template;
                    break;
            }
        });
    }
}

And the following is how it should be used (I use the pTemplate directive to get the TemplateRef for the body template, in the same way as PrimeNG does that).

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
      <app-my-table
          title="My table"
          [headers]="headers"
          [value]="data"
      >
          <ng-template pTemplate="body" let-item>
              <tr>
                  <td>{{item.a}}</td>
                  <td>{{item.b | uppercase }}</td>
                  <td>{{item.c | percent }}</td>
              </tr>
          </ng-template>
      </app-my-table>`,
})
export class AppComponent {

    headers = ['A', 'B', 'C'];
    data = [
        { a: 'a', b: 'b', c: 0.0},
        { a: 'a', b: 'b', c: 0.1},
        { a: 'a', b: 'b', c: 0.2},
        { a: 'a', b: 'b', c: 0.3},
        { a: 'a', b: 'b', c: 0.4},
        { a: 'a', b: 'b', c: 0.5},
    ];
}

I tried to insert bodyTemplate with <ng-template pTemplate="body" [ngTemplateOutlet]="bodyTemplate"></ng-template>, but that doesn't work. It might be an issue with a missing context (the error reads "Cannot read properties of undefined (reading 'a')"), but maybe I am just doing it wrong :).

Does anyone know how this can be achieved?


Solution

  • ngTemplateOutlet use with ng-container not ng-template

    should be

    <ng-container pTemplate="body" *ngTemplateOutlet="bodyTemplate; context: { $implicit: row}"></ng-container>

    and wrap it inside p-table default body template

    <ng-template pTemplate="body" let-row>

            <ng-template pTemplate="body" let-row>
              <ng-container pTemplate="body" *ngTemplateOutlet="bodyTemplate; context: { $implicit: row}"></ng-container>
            </ng-template>
    

    Demo here