DEV Community

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

Posted on

10 1 1 1 1

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

Sentry blog image

How I fixed 20 seconds of lag for every user in just 20 minutes.

Our AI agent was running 10-20 seconds slower than it should, impacting both our own developers and our early adopters. See how I used Sentry Profiling to fix it in record time.

Read more

Top comments (0)

Jetbrains image

Build Secure, Ship Fast

Discover best practices to secure CI/CD without slowing down your pipeline.

Read more

👋 Kindness is contagious

Dive into this thoughtful article, cherished within the supportive DEV Community. Coders of every background are encouraged to share and grow our collective expertise.

A genuine "thank you" can brighten someone’s day—drop your appreciation in the comments below!

On DEV, sharing knowledge smooths our journey and strengthens our community bonds. Found value here? A quick thank you to the author makes a big difference.

Okay