Angular Material Table with Dynamic Columns
November 09, 2023
Angular Material provides mat-table
directive to create table. Sorting and pagination is performed using matSort
and mat-paginator
directives respectively.
In this article, I am explaining how to create Material table that can change number of columns and its names at runtime. I will create a component that will handle creating tables, it will accept datasource, column names to display dynamically.
1. Create Child Component for Table with Dynamic Data
Create a child component to create table with dynamic data and columns. In component class, we create@Input()
properties to accept datasource, selected columns to display and their column names. Sorting and pagination will take place as usual.
dynamic-table.component.ts
import { Component, AfterViewInit, ViewChild, Input } from '@angular/core'; import { MatTableDataSource } from '@angular/material/table'; import { MatPaginator } from '@angular/material/paginator'; import { MatSort } from '@angular/material/sort'; import { Observable } from 'rxjs'; @Component({ selector: 'dynamic-table', templateUrl: './dynamic-table.component.html' }) export class DynamicTableComponent implements AfterViewInit { @ViewChild(MatSort) sort = {} as MatSort; @ViewChild(MatPaginator) paginator = {} as MatPaginator; @Input('dynamicColumns') dynamicColumns!: string[]; @Input('displayColumnNames') displayColumnNames!: string[]; @Input('dataToDisplay') dataToDisplay$!: Observable<any>; dataSource = new MatTableDataSource(); ngAfterViewInit() { this.dataToDisplay$.subscribe(data => { this.dataSource.data = data; this.dataSource.sort = this.sort; this.dataSource.paginator = this.paginator; }); } }
dynamic-table.component.html
<div class="mat-elevation-z8"> <table mat-table [dataSource]="dataSource" matSort> <ng-container *ngFor="let colName of dynamicColumns; index as i;" [matColumnDef]="colName"> <th mat-header-cell *matHeaderCellDef mat-sort-header> {{displayColumnNames[i]}} </th> <td mat-cell *matCellDef="let element"> {{element[colName]}} </td> </ng-container> <tr mat-header-row *matHeaderRowDef="dynamicColumns"></tr> <tr mat-row *matRowDef="let row; columns: dynamicColumns;"></tr> </table> <mat-paginator [pageSizeOptions]="[3, 5, 8]" showFirstLastButtons></mat-paginator> </div>
2. Create Service to Fetch Dynamic Data
Find the service class. Here I am creating methods to return dynamic data and columns to display. In my demo application, I am taking two sets of columns to display.article.service.ts
import { Injectable } from '@angular/core'; import { of } from 'rxjs'; import { Article } from './article'; const All_ARTICLES: Article[] = [ { id: 1, title: 'Angular Tutorial', category: 'Angular', writer: 'Mohit' }, { id: 2, title: 'Angular Material Tutorial', category: 'Angular', writer: 'Krishn' }, { id: 3, title: 'Spring tutorial', category: 'Spring', writer: 'Mohit' }, { id: 4, title: 'Hibernate tutorial', category: 'Hibernate', writer: 'Krishn' }, { id: 5, title: 'Java Tutorial', category: 'Java', writer: 'Sudesh' }, { id: 6, title: 'JavaScript Tutorial', category: 'JavaScript', writer: 'Shiv' } ]; @Injectable({ providedIn: 'root' }) export class ArticleService { getAllArticles() { return of(All_ARTICLES); } getDynamicColumns1() { return ['id', 'title', 'category', 'writer']; } getDisplayColumnNames1() { return ['Id', 'Title', 'Category', 'Writer']; } getDynamicColumns2() { return ['id', 'title']; } getDisplayColumnNames2() { return ['Id', 'Title']; } }
3. Create Parent Component to Pass Table Data to Child
Find the parent component that is using child component to create tables. For demo I am creating two tables.app.component.ts
import { Component, OnInit } from '@angular/core'; import { ArticleService } from './article.service'; import { Observable } from 'rxjs'; import { Article } from './article'; @Component({ selector: 'app-root', template: ` <h3>Table 1</h3> <dynamic-table [dynamicColumns]="dynamicColumns1" [displayColumnNames]="displayColNames1" [dataToDisplay]="articleData$"> </dynamic-table> <h3>Table 2</h3> <dynamic-table [dynamicColumns]="dynamicColumns2" [displayColumnNames]="displayColNames2" [dataToDisplay]="articleData$"> </dynamic-table> ` }) export class AppComponent implements OnInit { dynamicColumns1!: string[]; displayColNames1!: string[]; dynamicColumns2!: string[]; displayColNames2!: string[]; articleData$!: Observable<Article[]>; constructor(private articleService: ArticleService) { } ngOnInit() { this.articleData$ = this.articleService.getAllArticles(); this.dynamicColumns1 = this.articleService.getDynamicColumns1(); this.displayColNames1 = this.articleService.getDisplayColumnNames1(); this.dynamicColumns2 = this.articleService.getDynamicColumns2(); this.displayColNames2 = this.articleService.getDisplayColumnNames2(); } }
