DEV Community

Cover image for Dynamically render columns in Angular material table
Dharmen Shah for Angular Material Dev

Posted on

Dynamically render columns in Angular material table

Did you know that you can dynamically render columns in #angular material table?

No, I am not talking about simply adding/removing columns on click of button, but a more robust solution like shown in below.

<table-basic-example>
  <ng-container matColumnDef="boilingPoint">
    <th mat-header-cell *matHeaderCellDef mat-sort-header="boilingPoint">
      Boiling Point
    </th>
    <td mat-cell *matCellDef="let element">{{ element.boilingPoint }}</td>
  </ng-container>
</table-basic-example>
Enter fullscreen mode Exit fullscreen mode

As show in code above, it's upto consumer component which column it needs to render This technique involves mainly 2 things

1st is hostDirectives - to attach MatSort with component.

It is needed so that consumer can use mat-sort-header if it wants

@Component({
  selector: 'table-dynamic-example',
  standalone: true,
  imports: [MatTableModule, MatSortModule],
  hostDirectives: [MatSort],
})
Enter fullscreen mode Exit fullscreen mode

2nd is contentChildren - to get projected columns

Due to internal structure of MatTable, we can't directly use content projection here using ng-content, hence contentChildren is needed

private customCols = contentChildren(MatColumnDef);
Enter fullscreen mode Exit fullscreen mode

And finally render them in afterViewInit hook using MatTable.addColumnDef

Also make sure to attach host MatSort with dataSource's sort. Otherwise sorting will not work

ngAfterViewInit() {
    this.dataSource.sort = this.matSort;
    this.customCols().forEach((col) => {
      const columnName = col.name;
      this.matTable()?.addColumnDef(col);

      // need to wait until column is added
      this.ngZone.onStable.pipe(take(1)).subscribe(() => {
        this.columnsToDisplay.push(columnName);
      });
    });
  }
Enter fullscreen mode Exit fullscreen mode
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
  @for (column of displayedColumns; track column) {

  <ng-container [matColumnDef]="column">
    <th mat-header-cell *matHeaderCellDef mat-sort-header>{{ column }}</th>
    <td mat-cell *matCellDef="let element">{{ element[column] }}</td>
  </ng-container>
  }

  <tr mat-header-row *matHeaderRowDef="columnsToDisplay"></tr>
  <tr mat-row *matRowDef="let row; columns: columnsToDisplay;"></tr>
</table>
Enter fullscreen mode Exit fullscreen mode

That's it! Full code is available on stackblitz

Top comments (0)