Angular Material 9/8 DataTables, Pagination, Sorting, Filter & Fixed Columns Tutorial

Angular Material is a Material Design based UI library which provides a number of easy to use UI components. Angular Material data tables are used to display data in tabular format on pages.

In this tutorial, we will create a new Angular project using Angular CLI. Then we will install the Material library in an Angular project.

After application setup with Material, we will learn how to add Datatables and its important features like Pagination, column sorting, data filter and fixed sections on the table including headers and rows.

Let’s get started…

# Setup Angular CLI

We’ll create Angula project in the latest version. Make sure you have updated the Angular CLI tool by running below npm command in the terminal

$ npm install -g @angular/cli</pre>
 
<h3># Create an Angular Project</h3>
Execute below <code>ng command to create an Angular project in latest version 9.1.3. But this tutorial is compatible with previous version 7,6,5 and 4
$ ng new angular-material-checkboxlist
$ cd angular-material-checkboxlist</pre>
<h3></h3>
<h3># Install Angular Material in project</h3>
After version 8, Angular Material package can be installed by executing the following <code>ng command. For configuration, it will ask a few questions related to the theme, browser animations etc
$ ng add @angular/material</pre>
Answer questions
<pre class="wp-block-prismatic-blocks"><code class="language-javascript">? Choose a prebuilt theme name, or "custom" for a custom theme: Indigo/Pink
? Set up global Angular Material typography styles? Yes
? Set up browser animations for Angular Material? Yes</pre>
We are done with Angular material installation and configurations. Now our Angular project is ready to use Material components.

 
<h3># Import Material Modules</h3>
The Material UI library provides a wide variety of components, so we need to import the API module of the component we are going to use in the App module.

After adding Angular material in our project, next, we need to import <code><strong>MatTableModule </strong> and MatSortModule for sorting in the datatable in the app.module.ts then add in imports array as shown below.
// app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { MatTableModule } from '@angular/material/table';
import { MatSortModule } from '@angular/material/sort';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,

    MatTableModule,
    MatSortModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Now we can use Material Datatables in our application.

#Create Simple Datatable

Let's start by adding the simplest Datatable in our App component.

Open app.component.ts file then add <table mat-table></table> Or <mat-table></mat-table> as datatable wrapper.

After that, we define a dataSource attribute with a data object.

<mat-table [dataSource]="dataSource">

</mat-table></pre>
 

<strong>Define columns: </strong>Columns of data tables are defined by using the following template.
<pre class="wp-block-prismatic-blocks"><code class="language-javascript"><ng-container matColumnDef="userName">
  <mat-header-cell *matHeaderCellDef> Name </mat-header-cell>
  <mat-cell *matCellDef="let user"> {{user.name}} </mat-cell>
</ng-container></pre>
 

The <code><ng-container> is to define column definition having matColumnDef value which must be unique for a column.

<strong><mat-header-cell></strong> with <strong>*matHeaderCellDef</strong> contains the Text for column header.

<strong><mat-cell></strong> having object key of data passed as variable in <strong>*matCellDef</strong>

After that in <strong><mat-table></strong> we add following templates for Header and Row cell configuration
    <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
    <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row></pre>
<code><strong>displayedColumns</strong> will have an array list of columns we want to display in Datatables
displayedColumns: string[] = ['position', 'name', 'weight', 'symbol'];</pre>
 

Finally, our Simple Datatable will have the following template and component code.
<pre><code class="language-markup"><!-- app.component.html -->

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

    <!--- Note that these columns can be defined in any order.
          The actual rendered columns are set as a property on the row definition" -->
  
    <!-- Position Column -->
    <ng-container matColumnDef="position">
      <mat-header-cell *matHeaderCellDef> No. </mat-header-cell>
      <mat-cell *matCellDef="let element"> {{element.position}} </mat-cell>
    </ng-container>
  
    <!-- Name Column -->
    <ng-container matColumnDef="name">
      <mat-header-cell *matHeaderCellDef> Name </mat-header-cell>
      <mat-cell *matCellDef="let element"> {{element.name}} </mat-cell>
    </ng-container>
  
    <!-- Weight Column -->
    <ng-container matColumnDef="weight">
      <mat-header-cell *matHeaderCellDef> Weight </mat-header-cell>
      <mat-cell *matCellDef="let element"> {{element.weight}} </mat-cell>
    </ng-container>
  
    <!-- Symbol Column -->
    <ng-container matColumnDef="symbol">
      <mat-header-cell *matHeaderCellDef> Symbol </mat-header-cell>
      <mat-cell *matCellDef="let element"> {{element.symbol}} </mat-cell>
    </ng-container>
  
    <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
    <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
  </mat-table></pre>
 

Also, update the component class as shown below
<pre class="wp-block-prismatic-blocks"><code class="language-javascript">//app.component.ts

import { Component } from '@angular/core';


export interface PeriodicElement {
  name: string;
  position: number;
  weight: number;
  symbol: string;
}

const ELEMENT_DATA: PeriodicElement[] = [
  {position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H'},
  {position: 2, name: 'Helium', weight: 4.0026, symbol: 'He'},
  {position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li'},
  {position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be'},
  {position: 5, name: 'Boron', weight: 10.811, symbol: 'B'},
  {position: 6, name: 'Carbon', weight: 12.0107, symbol: 'C'},
  {position: 7, name: 'Nitrogen', weight: 14.0067, symbol: 'N'},
  {position: 8, name: 'Oxygen', weight: 15.9994, symbol: 'O'},
  {position: 9, name: 'Fluorine', weight: 18.9984, symbol: 'F'},
  {position: 10, name: 'Neon', weight: 20.1797, symbol: 'Ne'},
];


@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent  {

  displayedColumns: string[] = ['position', 'name', 'weight', 'symbol'];
  dataSource = ELEMENT_DATA;

}
</pre>
<h3></h3>
<h3># Pagination in Datatables</h3>
To add pagination in the datatable we need to import <code><strong>MatPaginatorModule</strong> in the app.module.ts file then add in the imports array

Then add <mat-paginator> immediately after <mat-table> tag as shown below
<!-- app.component.html -->

<mat-table [dataSource]="dataSource" class="mat-elevation-z8">
  <ng-container matColumnDef="position">
    <mat-header-cell *matHeaderCellDef> No. </mat-header-cell>
    <mat-cell *matCellDef="let element"> {{ element.position }} </mat-cell>
  </ng-container>

  <ng-container matColumnDef="name">
    <mat-header-cell *matHeaderCellDef> Name </mat-header-cell>
    <mat-cell *matCellDef="let element"> {{ element.name }} </mat-cell>
  </ng-container>

  <ng-container matColumnDef="weight">
    <mat-header-cell *matHeaderCellDef> Weight </mat-header-cell>
    <mat-cell *matCellDef="let element"> {{ element.weight }} </mat-cell>
  </ng-container>

  <ng-container matColumnDef="symbol">
    <mat-header-cell *matHeaderCellDef> Symbol </mat-header-cell>
    <mat-cell *matCellDef="let element"> {{ element.symbol }} </mat-cell>
  </ng-container>

  <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
  <mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
</mat-table>
<mat-paginator
  [pageSizeOptions]="[5, 10, 20]"
  showFirstLastButtons
></mat-paginator>

 

In the app.componet.ts file, we will import MatPaginator & MatTableDataSource and use it to get paginator instance as shown below

//app.component.ts

import { Component, ViewChild } from '@angular/core';

import { MatPaginator, MatTableDataSource } from '@angular/material';


export interface PeriodicElement {
  name: string;
  position: number;
  weight: number;
  symbol: string;
}

const ELEMENT_DATA: PeriodicElement[] = [
  { position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H' },
  { position: 2, name: 'Helium', weight: 4.0026, symbol: 'He' },
  { position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li' },
  { position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be' },
  { position: 5, name: 'Boron', weight: 10.811, symbol: 'B' },
  { position: 6, name: 'Carbon', weight: 12.0107, symbol: 'C' },
  { position: 7, name: 'Nitrogen', weight: 14.0067, symbol: 'N' },
  { position: 8, name: 'Oxygen', weight: 15.9994, symbol: 'O' },
  { position: 9, name: 'Fluorine', weight: 18.9984, symbol: 'F' },
  { position: 10, name: 'Neon', weight: 20.1797, symbol: 'Ne' },
];


@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {

  displayedColumns: string[] = ['position', 'name', 'weight', 'symbol'];
  dataSource = new MatTableDataSource<PeriodicElement>(ELEMENT_DATA);
  @ViewChild(MatPaginator) paginator: MatPaginator;

  ngOnInit() {
    this.dataSource.paginator = this.paginator;
  }

}

 

# Sorting on Datatable Columns

After that add <strong>matSort</strong> directive on mat-table wrapper then <strong>mat-sort-header </strong>on every column where you want sorting functionality.

<mat-table [dataSource]="dataSource" class="mat-elevation-z8" matSort>
  <ng-container matColumnDef="position">
    <mat-header-cell *matHeaderCellDef  mat-sort-header> No. </mat-header-cell>
    <mat-cell *matCellDef="let element"> {{ element.position }} </mat-cell>
  </ng-container>
...
...</pre>
In the <strong>app.componet.ts</strong> file in which we have Datatable, we will import <code>MatSort then use that instance as shown below
//app.component.ts

import { Component, ViewChild } from '@angular/core';

import { 
  MatSort, 
  MatPaginator, 
  MatTableDataSource } from '@angular/material';


export interface PeriodicElement {
  name: string;
  position: number;
  weight: number;
  symbol: string;
}

const ELEMENT_DATA: PeriodicElement[] = [
  { position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H' },
  { position: 2, name: 'Helium', weight: 4.0026, symbol: 'He' },
  { position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li' },
  { position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be' },
  { position: 5, name: 'Boron', weight: 10.811, symbol: 'B' },
  { position: 6, name: 'Carbon', weight: 12.0107, symbol: 'C' },
  { position: 7, name: 'Nitrogen', weight: 14.0067, symbol: 'N' },
  { position: 8, name: 'Oxygen', weight: 15.9994, symbol: 'O' },
  { position: 9, name: 'Fluorine', weight: 18.9984, symbol: 'F' },
  { position: 10, name: 'Neon', weight: 20.1797, symbol: 'Ne' },
];


@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {

  displayedColumns: string[] = ['position', 'name', 'weight', 'symbol'];
  dataSource = new MatTableDataSource<PeriodicElement>(ELEMENT_DATA);
  @ViewChild(MatPaginator) paginator: MatPaginator;
  @ViewChild(MatSort) sort: MatSort;

  ngOnInit() {
    this.dataSource.paginator = this.paginator;
    this.dataSource.sort = this.sort;
  }

}

 

# Filter Datatable Rows

To filter datatable rows we will add an input field with a (keyup) event listener, which will simply filter the <strong>dataSource</strong> object as shown below

<mat-form-field>
  <input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter">
</mat-form-field>

In app.component.ts file just add below method to filter datatable data.

....
....

  applyFilter(filterValue: string) {
    this.dataSource.filter = filterValue.trim().toLowerCase();
  }

# Sticky or Freeze Headers and Columns in Datatables

Sometime we may need to freeze Header or Footer rows or Columns with Checkboxes or actions. We can use sticky directive on our Headers, Footers or Columns as shown below.

Freeze Header:

<mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></mat-header-row>

Freeze Footer: 

<mat-footer-row *matFooterRowDef="displayedColumns; sticky: true"></mat-footer-row>

Freeze Columns:

    <ng-container matColumnDef="name" sticky>
      <mat-header-cell *matHeaderCellDef> Name </mat-header-cell>
      <mat-cell *matCellDef="let element"> {{element.name}} </mat-cell>
    </ng-container>

 

So here we quickly go through some powerful features available in Angular Material Datatables, you can check more details on docs here.

Leave a Comment

Your email address will not be published. Required fields are marked *