Tech Incent
Angular

Angular material data table, sort, pagination, filter – complete example

angular-material-table

In this post, I will implement a material data table with API call and also show material table sort, pagination, filter feature.

Start a new project from scratch

ng new angular-mat-table
cd angular-mat-table

Add angular material dependancy

ng add @angular/material

Import material modules

In the app.module.ts file add modules.

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { HttpClientModule } from '@angular/common/http';

import { MatTableModule } from '@angular/material/table';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatInputModule } from '@angular/material/input';
import { MatSortModule } from '@angular/material/sort';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
  
@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    BrowserAnimationsModule,
    HttpClientModule,
    MatTableModule,
    MatInputModule,
    MatPaginatorModule,
    MatSortModule,
    MatButtonModule,
    MatIconModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

MatTableModule: MatTableModule is the core module for the material table. MatTabTable module container all table directives. Some examples: mat-table, MatCellDef, MatHeaderCellDef

MatSortModule: MatSortModule allows matTable header sortable. this is also an optional module.

MatPaginatorModule: MatPaginationModile is generic module of material. It uses for paginate data and this module helps to paginate the material table data.

MatInputModule: I am going to show the material table data filter example, which needs input so I will use the material input field. It’s an optional module for table

HttpClientModule: httpClintModule is an angular generic module which use for API calls.

RestOfModules import: MatButtonModule, MatIconModule, MatProgressSpinnerModule is also a generic module that can be used for the material button, icon, and loader

Fetch table data and prepare for material table

In app.componenet.ts file. prepare material table data

import { HttpClient } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { MatTableDataSource } from '@angular/material/table';

export interface Post {
  id: number;
  title: string;
  body: string;
  userId: number;
}

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
  displayedColumns: string[] = ['id', 'title', 'body', 'userId', 'actions'];
  dataSource = new MatTableDataSource<Post>([]);

  constructor(private http: HttpClient) { }
  ngOnInit(): void {
    this.fetchPosts()
    console.log('DataSource', this.dataSource)
  }
  fetchPosts(): void {
    this.http.get('https://jsonplaceholder.typicode.com/posts').subscribe((data) => {
      this.dataSource.data = data as Post[];
    })
  }
}
Explanation

Interface: First of all declare post interface

MatTableDataSource: We are going to use a build-in matTableDataSource table data source.

displayed columns: display columns material table columns identity.

Datasource: Initial empty list of the data sources.

FetchPosts: “fetchPosts” methods fetch all post data from API. and insert dataSource. call fechPosts method in OnInit component circle which it will call before component view

Note: this is not a good way to call API in the component. best way to call API from particular service.

let’s what is is in-side build-in MatTableDataSource.

To start server: ng serve

MatTableDataSource
MatTableDataSource contain

Let’s implement material table in HTML

This data table list of posts that has five columns (id, title, description, userId, action buttons)

Edit app.component.html file

<div class="container">
  <table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
    <!-- Id Column -->
    <ng-container matColumnDef="id">
      <th mat-header-cell *matHeaderCellDef> No. </th>
      <td mat-cell *matCellDef="let element"> {{element.id}} </td>
    </ng-container>
    <!-- <mat-text-column name="id"></mat-text-column> -->
  
    <!-- Title Column -->
    <ng-container matColumnDef="title">
      <th mat-header-cell *matHeaderCellDef> Title </th>
      <td mat-cell *matCellDef="let element"> {{element.title | slice: 0:30}} </td>
    </ng-container>
  
    <!-- Body Column -->
    <ng-container matColumnDef="body">
      <th mat-header-cell *matHeaderCellDef> Description </th>
      <td mat-cell *matCellDef="let element"> {{element.body | slice: 0:50}} </td>
    </ng-container>
  
    <!-- UserId Column -->
    <ng-container matColumnDef="userId">
      <th mat-header-cell *matHeaderCellDef> User </th>
      <td mat-cell *matCellDef="let element"> {{element.userId}} </td>
    </ng-container>
    <!-- Actions Column -->
    <ng-container matColumnDef="actions">
      <th mat-header-cell *matHeaderCellDef style="text-align: right;"> Actions </th>
      <td mat-cell *matCellDef="let element">
        <div style="display: flex;align-items: center;justify-content: flex-end;">
          <button mat-flat-button color="primary" style="margin-right: 10px;"><mat-icon>edit</mat-icon></button>
          <button mat-flat-button color="warn"><mat-icon>delete</mat-icon></button>
        </div>
      </td>
    </ng-container>
  
    <tr mat-header-row *matHeaderRowDef="displayedColumns, sticky: true"></tr>
    <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
  </table>
</div>

Fix table and container size

By default table tag has no widh limit. without widh table data is collapsed each columns. So add table size

Container is fully optional. I was implement container to display table center

In the app.component.scss file.

.container {
  padding: 50px 20px;
  width: 100%;
  max-width: 1440px;
  margin: 0 auto;
}

table {
  width: 100%;
}

Explain material table column

    <!-- Title Column -->
    <ng-container matColumnDef="title">
      <th mat-header-cell *matHeaderCellDef mat-sort-header> Title </th>
      <td mat-cell *matCellDef="let element"> {{element.title | slice: 0:30}} </td>
    </ng-container>

We can see each column defined with which has the matColumnDef directive and unique columns identifier as columns name. If any column display only just plain text you can use mat-text-column element with name attribute. but name an attribute must be a unique identifier as column name

Each column should contain a table header with mat-header-cell directive and row cell with mat-cell and *matCellDef directive.

Explain material row template

    <tr mat-header-row *matHeaderRowDef="displayedColumns, sticky: true"></tr>
    <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>

So when defined material columns to render header and data row, we need to define rows header and data row.

Implement material table pagination

Material table MatTableDataSource provide build-in pagination. it need to attach pagination with table.

After table, add material pagination in app.component.html file.

<div class="container">
  <table mat-table [dataSource]="dataSource" class="mat-elevation-z8">

  </table>
  <mat-paginator [pageSizeOptions]="[10, 25, 50, 100]" showFirstLastButtons></mat-paginator>
</div>

Attach pagination with material MatTableDataSource. Modify app.component.ts file

// ...
import { MatPaginator } from '@angular/material/paginator';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
  // ...
  @ViewChild(MatPaginator, {static: true}) paginator!: MatPaginator;
  constructor(private http: HttpClient) { }
  ngOnInit(): void {
    this.dataSource.paginator = this.paginator;
    // ...
  }
  // ...
}

Implement material table sort

Attach sort with Material MatTableDataSource, modify the app.component.ts file

// ...
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort'

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
  // ...
  @ViewChild(MatPaginator, {static: true}) paginator!: MatPaginator;
  @ViewChild(MatSort, { static: true }) sort!: MatSort;
  constructor(private http: HttpClient) { }
  ngOnInit(): void {
    this.dataSource.paginator = this.paginator;
    this.dataSource.sort = this.sort;
    // ...
  }
  // ...
}

Modify app.component.html file

In my case, I was used MatTableDataSource. So It needs to define the matSort directive in the table. And we need to add mat-header-sort directive in header columns cell

<div class="container">
  <table mat-table [dataSource]="dataSource" class="mat-elevation-z8" matSort>
    <!-- Id Column -->
    <ng-container matColumnDef="id">
      <th mat-header-cell *matHeaderCellDef mat-sort-header> No. </th>
      <td mat-cell *matCellDef="let element"> {{element.id}} </td>
    </ng-container>

    <!-- Title Column -->
    <ng-container matColumnDef="title">
      <th mat-header-cell *matHeaderCellDef mat-sort-header> Title </th>
      <td mat-cell *matCellDef="let element"> {{element.title | slice: 0:30}} </td>
    </ng-container>
  </table>
</div>

Implement material table filter

Material table filter uses a generic strategy. when the user types input and MatTableDataSource filter property listen to the input value which is filter data in dataSource

Implement listener MatTableDataSource filter

Modify app.component.ts file. Define applyFilter method to filter dataSource data

// ...

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
  // ...
  fetchPosts(): void {
    this.http.get('https://jsonplaceholder.typicode.com/posts').subscribe((data) => {
      this.dataSource.data = data as Post[];
    })
  }
  applyFilter(event: Event) {
    const filterValue = (event.target as HTMLInputElement).value;
    this.dataSource.filter = filterValue.trim().toLowerCase();

    if (this.dataSource.paginator) {
      this.dataSource.paginator.firstPage();
    }
  }
}

Implement filter input field

Modify app.component.ts file.

Note: For example use the matInput field. so make sure you import MatInputModule from material

<div class="container">
  <mat-form-field appearance="standard">
    <mat-label>Filter</mat-label>
    <input matInput (keyup)="applyFilter($event)" placeholder="Ex. esse" #input>
  </mat-form-field>
  <table mat-table [dataSource]="dataSource" class="mat-elevation-z8" matSort>
  </table>
</div>

Checkout example code in GitHub: https://github.com/techincent/angular-material-table

Related posts

Angular Production-Ready Project Setup

Sajal Mia

Angular bootstrap date picker example

Sajal Mia