Angular Material 12 Table, Sorting, Searching & Pagination

Angular Material is a high quality UI Components to use as an alternate for Bootstrap or any other UI frameworks. Angular Material provides various components to work with. It has its own set of components like Data Tables, Buttons, Popups, Cards and many more.

More info about Angular is here: Angular Material Official Link

In this article we will learn about Angular Material, Data Table, Data Sources, matColumnDef, Sort and Pagination details. I have used Angular 12 and Angular Material 12.2.8 for this example.

Download the complete code from Github here: Repo Link


Before starting the article, let’s look into some common questions about Angular Material.

What is Angular Material Table?

It is a table that provides mat designed data tables in rows and columns. Its similar to HTML table but with more features and designs. It has its own tags and attributes.

What is DataSource in Angular Material?

A datasource is a combined code to handle data retrieval, sorting, filtering, pagination etc things. It contains all logics to work with a DataTable in Angular Material.

What is matColumnDef in Angular?

It is a column definition in mat-table. It defines the set of cells available for the column in material table.


Let’s start the article.

Sit tight! You are going to start a pretty big tutorial 🙂

Create Angular Material DataTable

Add Material into your Angular project


ng add @angular/material
Output of Material Installations
Output of Material Installations

Import Table Module

Import the material modules into app.module.ts file (or any other file in case you are using a shared module file). As we need to implement the sorting as well so I am importing the MatSortModule along with MatCardModule (Card Module will make the UI a little better).


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

@NgModule ({
  imports: [
    MatTableModule,
    MatSortModule,
    MatCardModule,
    // other modules here
  ]
})
class AppModule {}

Create a Service file

Next we have to create a app.service.ts file with getSampleData method. I am using Github Search API to fetch the data. You can replace it with your actual API.


import { Injectable } from "@angular/core";
import { HttpClient } from '@angular/common/http';
import { Observable } from "rxjs";
import { GithubApi } from "./table/table.component";
import { SortDirection } from "@angular/material/sort";

@Injectable()
export class AppService {
    constructor(private httpClient: HttpClient) {
    }

    getSampleData(sort: string, order: SortDirection, page: number): Observable {
        return this.httpClient.get(`https://api.github.com/search/issues?q=repo:angular/components&sort=${sort}&order=${order}&page=${page + 1}`);
    }
}

Create Component and Consume API


import { AfterViewInit, Component, ViewChild } from '@angular/core';
import { MatSort } from '@angular/material/sort';
import { of } from 'rxjs';
import { startWith, switchMap, catchError, map } from 'rxjs/operators';
import { AppService } from '../app.service';

export interface GithubApi {
  items: GithubIssue[];
  total_count: number;
}

export interface GithubIssue {
  created_at: string;
  number: string;
  state: string;
  title: string;
}

@Component({
  selector: 'app-table',
  templateUrl: './table.component.html',
  styleUrls: ['./table.component.scss']
})
export class TableComponent implements AfterViewInit {
  displayedColumns: string[] = ['created', 'state', 'number', 'title'];
  data: GithubIssue[] = [];
  @ViewChild(MatSort) sort!: MatSort;

  constructor(private appService: AppService) { }

  ngAfterViewInit() {
    // If the user changes the sort order, reset back to the first page.
    this.sort.sortChange.subscribe(() => 0);

    this.sort.sortChange
      .pipe(
        startWith({}),
        switchMap(() => {
          return this.appService!.getSampleData(this.sort.active, this.sort.direction, 0)
            .pipe(catchError(() => of(null)));
        }),
        map(data => {
          if (data === null) {
            return [];
          }

          return data.items;
        })
      ).subscribe(data => this.data = data);
  }
}

Code Explanation: Two interfaces are used as Models- GithubApi and GithubIssue. @ViewChild(MatSort) sort!: MatSort; is used to capture the sort click from UI. displayedColumns is used to hold the array of string values to bind Header Dynamically.

Note: We need to use ngAfterViewInit instead of ngOnInit because we need to capture matSort from UI. If we use ngOnInit instead of ngAfterViewInit then it will throw following error:

Error when using code inside ngOnInit
Error when using code inside ngOnInit

Next we need to capture sortChange on matSort. startWith is used to know when subscription has occurred on an existing observable. switchMap is used to switch to the new observable when sort is clicked before completing its previous request. map is used to check whether response is null. If yes then return blank array so that UI can not complain about null data error.

Create Template and bind Mat Data Table



    Mat DataTable
    
        
# {{row.number}} Title {{row.title}} State {{row.state}} Created {{row.created_at | date}}

Run the app and check view

Now its time to run the app and check whether we have done things right. You will see following output:

Angular Material Data Table with Sorting
Angular Material Data Table with Sorting

You will get your Material Data Table working with Dynamic Header, Rows and Sorting functionalities. Nice right!


Our Angular Material Dynamic Data Table is complete with Sorting. Its time to implement Searching into it.

Searching in Angular Material Data Table

The next thing we are going to implement is: Server Side Searching in Material Data Table.

Update getSampleData method

Modify your getSampleData file inside app.service.ts to accept a 4th parameter, q. We will pass searchTerm in the 4th parameter.


getSampleData(sort: string, order: SortDirection, page: number, q: string): Observable {
        return this.httpClient.get(`https://api.github.com/search/issues?q=${q}&sort=${sort}&order=${order}&page=${page + 1}`);
}

Update Component


displayedColumns: string[] = ['created', 'state', 'number', 'title'];
  data: GithubIssue[] = [];
  @ViewChild(MatSort) sort!: MatSort;
  term$ = new BehaviorSubject('');
  constructor(private appService: AppService) { }

  ngAfterViewInit() {
    // If the user changes the sort order, reset back to the first page.
    this.sort.sortChange.subscribe(() => 0);

    merge(this.sort.sortChange, this.term$.pipe(debounceTime(1000), distinctUntilChanged()))
      .pipe(
        startWith({}),
        switchMap((searchTerm) => {
          return this.appService!.getSampleData(this.sort.active, this.sort.direction, 0, (searchTerm && typeof searchTerm == 'string') ? searchTerm.toString() : 'repo:angular/components')
            .pipe(catchError(() => of(null)));
        }),
        map(data => {
          if (data === null) {
            return [];
          }

          return data.items;
        })
      ).subscribe(data => this.data = data);
}

Here I have used RxJS merge operator to merge Sort and Search subscriptions together. debounceTime is used to wait for 1 sec when user types. distinctUntilChanged is used to call search API only if your search query is different than previous.


(searchTerm && typeof searchTerm == 'string') ? searchTerm.toString() : 'repo:angular/components'

Above condition is used to check whether the search term is string, not Sort (because I have combined search and sort together).

Update template

Include a text box for search.


 Term-
        {{term$|async}}

Run the page again and go to browser. Your output will look like this:

Server Side Search with Angular Data Table

Yayyyy! Your Server Side Search functionality is implement and working properly.


Now the next thing to implement is: Server Side Pagination Functionality

Server Side Pagination Functionality

Pagination is the way to display data page by page instead of all at one page. Server Side Pagination improves website speed. Check 5 Best Ways To Optimize Angular For Scaling 

Import Pagination Module

Import Paginator Module in app.module.ts file


import {MatProgressBarModule} from '@angular/material/progress-bar';

Update Component for Paging

Update your Component file with below code:


 displayedColumns: string[] = ['created', 'state', 'number', 'title'];
  data: GithubIssue[] = [];
  @ViewChild(MatSort) sort!: MatSort;
  term$ = new BehaviorSubject('');
  resultsLength = 0;
  @ViewChild(MatPaginator) paginator!: MatPaginator;
  constructor(private appService: AppService) { }

  ngAfterViewInit() {
    // If the user changes the sort order, reset back to the first page.
    this.sort.sortChange.subscribe(() => this.paginator.pageIndex = 0);

    merge(this.sort.sortChange, this.term$.pipe(debounceTime(1000), distinctUntilChanged()), this.paginator.page)
      .pipe(
        startWith({}),
        switchMap((searchTerm) => {
          return this.appService!.getSampleData(this.sort.active, this.sort.direction, this.paginator.pageIndex, (searchTerm && typeof searchTerm == 'string') ? searchTerm.toString() : 'repo:angular/components')
            .pipe(catchError(() => of(null)));
        }),
        map(data => {
          if (data === null) {
            return [];
          }

          this.resultsLength = data.total_count;
          return data.items;
        })
      ).subscribe(data => this.data = data);
  }

You will notice this.paginator.page is merged with other two Events. This way we don’t need to create any separate click events for Paging. The merge operator will merge all the Events and will emit event if any of those will change its value.

Add Paginator in Template

Add mat-paginator tag in html template.




Paginator Code in HTML
Paginator Code in HTML

Wow! You are here means you have done pretty big task today 🙂 . Anyways, let’s check the output now.

Server Side Pagination with Angular Material Data Table
Server Side Pagination with Angular Material Data Table

Cool! Our Pagination is working. You did it!!

Conclusion

In this article we learnt:

  • Create a Dynamic Angular Material Data Table with Sorting
  • Implement Server Side Search in Material Data Table
  • Implement Pagination in Material Data Table

Well, that’s all for now. If you liked the article, do share with other programmers.

Download the complete code from Github here: Repo Link

Must Read:
RxJS forkJoin: Definition and Real World Uses
Real World Examples of 5 Common Observable Operators
5 Best Ways To Optimize Angular For Scaling (2021)
7 Best Ways To Improve Angular Code (2021)
5 Great SEO Ideas To Improve Angular Website Ranking

Leave a Reply

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