Angular Material Table with Dynamic Columns

By Arvind Rai, 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;
        });
    }
} 
In HTML template, iterate column array using for loop.
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();    
  }
} 
Output
Angular Material Table with Dynamic Columns

4. Reference

5. Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI
LEARN MORE







©2024 concretepage.com | Privacy Policy | Contact Us