Datatables

Angular Bootstrap 5 Datatables

The Datatable is a component which mix tables with advanced options like searching, sorting and pagination.

Note: Read the API tab to find all available options and advanced customization

Video tutorial


Basic example

Name Position Office Age Start date Salary
Tiger NixonSystem ArchitectEdinburgh612011/04/25$320,800
Sonya FrostSoftware EngineerEdinburgh232008/12/13$103,600
Jena GainesOffice ManagerLondon302008/12/19$90,560
Quinn FlynnSupport LeadEdinburgh222013/03/03$342,000
Charde MarshallRegional DirectorSan Francisco362008/10/16$470,600
Haley KennedySenior Marketing DesignertLondon432012/12/18$313,500
Tatyana FitzpatrickRegional DirectorLondon192010/03/17$385,750
Michael SilvaMarketing DesignerLondon662012/11/27$198,500
Paul ByrdChief Financial Officer (CFO)New York642010/06/09$725,000
Gloria LittleSystems AdministratorNew York592009/04/10$237,500

Rows per page:

10
1 - 10 of 16
<div class="datatable mt-4">
  <table
    class="table datatable-table"
    mdbTable
    mdbTableSort
    #table="mdbTable"
    #sort="mdbTableSort"
    [dataSource]="dataSource"
    [pagination]="pagination"
    [sort]="sort"
  >
    <thead class="datatable-header">
      <tr>
        <th *ngFor="let header of headers" [mdbTableSortHeader]="header" scope="col">
          {{ header | titlecase }}
        </th>
      </tr>
    </thead>
    <tbody class="datatable-body">
      <tr *ngFor="let data of table.data" scope="row">
        <td>
          {{ data.name }}
        </td>
        <td>
          {{ data.position }}
        </td>
        <td>
          {{ data.office }}
        </td>
        <td>
          {{ data.age }}
        </td>
        <td>
          {{ data.startDate }}
        </td>
        <td>
          {{ data.salary }}
        </td>
      </tr>
    </tbody>
  </table>
  <mdb-table-pagination #pagination></mdb-table-pagination>
</div>
import { Component } from '@angular/core';

export interface Person {
  name: string;
  position: string;
  office: string;
  age: number;
  startDate: string;
  salary: string;
}

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

  headers = ['Name', 'Position', 'Office', 'Age', 'Start Date', 'Salary'];

  dataSource: Person[] = [
    {
      name: 'Tiger Nixon',
      position: 'System Architect',
      office: 'Edinburgh',
      age: 61,
      startDate: '2011/04/25',
      salary: '$320,800',
    },
    {
      name: 'Sonya Frost',
      position: 'Software Engineer',
      office: 'Edinburgh',
      age: 23,
      startDate: '2008/12/13',
      salary: '$103,600',
    },
    {
      name: 'Jena Gaines',
      position: 'Office Manager',
      office: 'London',
      age: 30,
      startDate: '2008/12/19',
      salary: '$90,560',
    },
    {
      name: 'Quinn Flynn',
      position: 'Support Lead',
      office: 'Edinburgh',
      age: 22,
      startDate: '2013/03/03',
      salary: '$342,000',
    },
    {
      name: 'Charde Marshall',
      position: 'Regional Director',
      office: 'San Francisco',
      age: 36,
      startDate: '2008/10/16',
      salary: '$470,600',
    },
    {
      name: 'Haley Kennedy',
      position: 'Senior Marketing Designer',
      office: 'London',
      age: 43,
      startDate: '2012/12/18',
      salary: '$313,500',
    },
    {
      name: 'Tatyana Fitzpatrick',
      position: 'Regional Director',
      office: 'London',
      age: 19,
      startDate: '2010/03/17',
      salary: '$385,750',
    },
    {
      name: 'Michael Silva',
      position: 'Marketing Designer',
      office: 'London',
      age: 66,
      startDate: '2012/11/27',
      salary: '$198,500',
    },
    {
      name: 'Paul Byrd',
      position: 'Chief Financial Officer (CFO)',
      office: 'New York',
      age: 64,
      startDate: '2010/06/09',
      salary: '$725,000',
    },
    {
      name: 'Gloria Little',
      position: 'Systems Administrator',
      office: 'New York',
      age: 59,
      startDate: '2009/04/10',
      salary: '$237,500',
    },
    {
      name: 'Garrett Winters',
      position: 'Accountant',
      office: 'Tokyo',
      age: 63,
      startDate: '2011/07/25',
      salary: '$170,750',
    },
    {
      name: 'Ashton Cox',
      position: 'Junior Technical Author',
      office: 'San Francisco',
      age: 66,
      startDate: '2009/01/12',
      salary: '$86,000',
    },
    {
      name: 'Cedric Kelly',
      position: 'Senior Javascript Developer',
      office: 'Edinburgh',
      age: 22,
      startDate: '2012/03/29',
      salary: '$433,060',
    },
    {
      name: 'Airi Satou',
      position: 'Accountant',
      office: 'Tokyo',
      age: 33,
      startDate: '2008/11/28',
      salary: '$162,700',
    },
    {
      name: 'Brielle Williamson',
      position: 'Integration Specialist',
      office: 'New York',
      age: 61,
      startDate: '2012/12/02',
      salary: '$372,000',
    },
  ];
}



Selectable rows

You can easily build datatable with selectable rows.

Name Position Office Age Start date Salary
Tiger NixonSystem ArchitectEdinburgh612011/04/25$320,800
Garrett WintersAccountantTokyo632011/07/25$170,750
Ashton CoxJunior Technical AuthorSan Francisco662009/01/12$86,000
Cedric KellySenior Javascript DeveloperEdinburgh222012/03/29$433,060
Airi SatouAccountantTokyo332008/11/28$162,700
Brielle WilliamsonIntegration SpecialistNew York612012/12/02$372,000
Herrod ChandlerSales AssistantSan Francisco592012/08/06$137,500
Rhona DavidsonIntegration SpecialistTokyo552010/10/14$327,900
Colleen HurstJavascript DeveloperSan Francisco392009/09/15$205,500
Sonya FrostSoftware EngineerEdinburgh232008/12/13$103,600

Rows per page:

10
1 - 10 of 14
<div class="datatable mt-4">
  <table
  class="table datatable-table"
  mdbTable
  mdbTableSort
  #table="mdbTable"
  #sort="mdbTableSort"
  [dataSource]="dataSource"
  [sort]="sort"
  [pagination]="pagination"
  >
    <thead class="datatable-header">
      <tr>
        <th scope="col">
          <div class="form-check d-flex align-items-center mb-0">
            <input
            mdbCheckbox
            class="datatable-header-checkbox form-check-input"
            type="checkbox"
            [checked]="allRowsSelected()"
            (checkboxChange)="toggleAll($event)"
            />
          </div>
        </th>
        <th *ngFor="let header of headers" [mdbTableSortHeader]="header" scope="col">
          {{ header | titlecase }}
        </th>
      </tr>
    </thead>
    <tbody class="datatable-body">
      <tr
      *ngFor="let data of table.data"
        scope="row"
        [class.active]="selections.has(data)"
      >
        <td>
          <div class="form-check">
            <input
            mdbCheckbox
            (click)="$event.stopPropagation()"
            (checkboxChange)="toggleSelection($event, data)"
            [checked]="selections.has(data)"
            class="datatable-row-checkbox form-check-input"
            type="checkbox"
            />
          </div>
        </td>
        <td>
          {{ data.name }}
        </td>
        <td>
          {{ data.position }}
        </td>
        <td>
          {{ data.office }}
        </td>
        <td>
          {{ data.age }}
        </td>
        <td>
          {{ data.startDate }}
        </td>
        <td>
          {{ data.salary }}
        </td>
      </tr>
    </tbody>
  </table>
  <mdb-table-pagination #pagination></mdb-table-pagination>
</div>
import { Component } from '@angular/core';
import { MdbCheckboxChange } from 'mdb-angular-ui-kit/checkbox';

export interface Person {
  name: string;
  position: string;
  office: string;
  age: number;
  startDate: string;
  salary: string;
}

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

  selections = new Set<Person>();

  headers = ['Name', 'Position', 'Office', 'Age', 'Start Date', 'Salary'];

  dataSource: Person[] = [
    {
      name: 'Tiger Nixon',
      position: 'System Architect',
      office: 'Edinburgh',
      age: 61,
      startDate: '2011/04/25',
      salary: '$320,800',
    },
    {
      name: 'Sonya Frost',
      position: 'Software Engineer',
      office: 'Edinburgh',
      age: 23,
      startDate: '2008/12/13',
      salary: '$103,600',
    },
    {
      name: 'Jena Gaines',
      position: 'Office Manager',
      office: 'London',
      age: 30,
      startDate: '2008/12/19',
      salary: '$90,560',
    },
    {
      name: 'Quinn Flynn',
      position: 'Support Lead',
      office: 'Edinburgh',
      age: 22,
      startDate: '2013/03/03',
      salary: '$342,000',
    },
    {
      name: 'Charde Marshall',
      position: 'Regional Director',
      office: 'San Francisco',
      age: 36,
      startDate: '2008/10/16',
      salary: '$470,600',
    },
    {
      name: 'Haley Kennedy',
      position: 'Senior Marketing Designer',
      office: 'London',
      age: 43,
      startDate: '2012/12/18',
      salary: '$313,500',
    },
    {
      name: 'Tatyana Fitzpatrick',
      position: 'Regional Director',
      office: 'London',
      age: 19,
      startDate: '2010/03/17',
      salary: '$385,750',
    },
    {
      name: 'Michael Silva',
      position: 'Marketing Designer',
      office: 'London',
      age: 66,
      startDate: '2012/11/27',
      salary: '$198,500',
    },
    {
      name: 'Paul Byrd',
      position: 'Chief Financial Officer (CFO)',
      office: 'New York',
      age: 64,
      startDate: '2010/06/09',
      salary: '$725,000',
    },
    {
      name: 'Gloria Little',
      position: 'Systems Administrator',
      office: 'New York',
      age: 59,
      startDate: '2009/04/10',
      salary: '$237,500',
    },
    {
      name: 'Garrett Winters',
      position: 'Accountant',
      office: 'Tokyo',
      age: 63,
      startDate: '2011/07/25',
      salary: '$170,750',
    },
    {
      name: 'Ashton Cox',
      position: 'Junior Technical Author',
      office: 'San Francisco',
      age: 66,
      startDate: '2009/01/12',
      salary: '$86,000',
    },
    {
      name: 'Cedric Kelly',
      position: 'Senior Javascript Developer',
      office: 'Edinburgh',
      age: 22,
      startDate: '2012/03/29',
      salary: '$433,060',
    },
    {
      name: 'Airi Satou',
      position: 'Accountant',
      office: 'Tokyo',
      age: 33,
      startDate: '2008/11/28',
      salary: '$162,700',
    },
    {
      name: 'Brielle Williamson',
      position: 'Integration Specialist',
      office: 'New York',
      age: 61,
      startDate: '2012/12/02',
      salary: '$372,000',
    },
  ];

  allRowsSelected(): boolean {
    const selectionsLength = this.selections.size;
    const dataLength = this.dataSource.length;
    return selectionsLength === dataLength;
  }

  toggleSelection(event: MdbCheckboxChange, value: Person): void {
    if (event.checked) {
      this.select(value);
    } else {
      this.deselect(value);
    }
  }

  toggleAll(event: MdbCheckboxChange): void {
    if (event.checked) {
      this.dataSource.forEach((row: Person) => {
        this.select(row);
      });
    } else {
      this.dataSource.forEach((row: Person) => {
        this.deselect(row);
      });
    }
  }

  select(value: Person): void {
    if (!this.selections.has(value)) {
      this.selections.add(value);
    }
  }

  deselect(value: Person): void {
    if (this.selections.has(value)) {
      this.selections.delete(value);
    }
  }
}

Scroll

Setting maximum height/width will enable vertical/horizontal scrolling.

Name Position Office Age Start date Salary
Tiger NixonSystem ArchitectEdinburgh612011/04/25$320,800
Garrett WintersAccountantTokyo632011/07/25$170,750
Ashton CoxJunior Technical AuthorSan Francisco662009/01/12$86,000
Cedric KellySenior Javascript DeveloperEdinburgh222012/03/29$433,060
Airi SatouAccountantTokyo332008/11/28$162,700
Brielle WilliamsonIntegration SpecialistNew York612012/12/02$372,000
Herrod ChandlerSales AssistantSan Francisco592012/08/06$137,500
Rhona DavidsonIntegration SpecialistTokyo552010/10/14$327,900
Colleen HurstJavascript DeveloperSan Francisco392009/09/15$205,500
Sonya FrostSoftware EngineerEdinburgh232008/12/13$103,600

Rows per page:

10
1 - 10 of 14
<div class="datatable mt-4">
  <div
    class="datatable-inner"
    mdbScrollbar
    style="position: relative; width: 600px; height: 500px"
  >
    <table
      class="table datatable-table"
      mdbTable
      mdbTableSort="mdbTableSort"
      #table="mdbTable"
      #sort="mdbTableSort"
      [dataSource]="dataSource"
      [sort]="sort"
      [pagination]="pagination"
    >
      <thead class="datatable-header">
        <tr>
          <th *ngFor="let header of headers" [mdbTableSortHeader]="header" scope="col">
            {{ header | titlecase }}
          </th>
        </tr>
      </thead>
      <tbody class="datatable-body">
        <tr *ngFor="let data of table.data" scope="row">
          <td>
            {{ data.name }}
          </td>
          <td>
            {{ data.position }}
          </td>
          <td>
            {{ data.office }}
          </td>
          <td>
            {{ data.age }}
          </td>
          <td>
            {{ data.startDate }}
          </td>
          <td>
            {{ data.salary }}
          </td>
        </tr>
      </tbody>
    </table>
  </div>
  <mdb-table-pagination #pagination style="width: 600px"></mdb-table-pagination>
</div>
import { Component } from '@angular/core';

export interface Person {
  name: string;
  position: string;
  office: string;
  age: number;
  startDate: string;
  salary: string;
}

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

  headers = ['Name', 'Position', 'Office', 'Age', 'Start Date', 'Salary'];

  dataSource: Person[] = [
    {
      name: 'Tiger Nixon',
      position: 'System Architect',
      office: 'Edinburgh',
      age: 61,
      startDate: '2011/04/25',
      salary: '$320,800',
    },
    {
      name: 'Sonya Frost',
      position: 'Software Engineer',
      office: 'Edinburgh',
      age: 23,
      startDate: '2008/12/13',
      salary: '$103,600',
    },
    {
      name: 'Jena Gaines',
      position: 'Office Manager',
      office: 'London',
      age: 30,
      startDate: '2008/12/19',
      salary: '$90,560',
    },
    {
      name: 'Quinn Flynn',
      position: 'Support Lead',
      office: 'Edinburgh',
      age: 22,
      startDate: '2013/03/03',
      salary: '$342,000',
    },
    {
      name: 'Charde Marshall',
      position: 'Regional Director',
      office: 'San Francisco',
      age: 36,
      startDate: '2008/10/16',
      salary: '$470,600',
    },
    {
      name: 'Haley Kennedy',
      position: 'Senior Marketing Designer',
      office: 'London',
      age: 43,
      startDate: '2012/12/18',
      salary: '$313,500',
    },
    {
      name: 'Tatyana Fitzpatrick',
      position: 'Regional Director',
      office: 'London',
      age: 19,
      startDate: '2010/03/17',
      salary: '$385,750',
    },
    {
      name: 'Michael Silva',
      position: 'Marketing Designer',
      office: 'London',
      age: 66,
      startDate: '2012/11/27',
      salary: '$198,500',
    },
    {
      name: 'Paul Byrd',
      position: 'Chief Financial Officer (CFO)',
      office: 'New York',
      age: 64,
      startDate: '2010/06/09',
      salary: '$725,000',
    },
    {
      name: 'Gloria Little',
      position: 'Systems Administrator',
      office: 'New York',
      age: 59,
      startDate: '2009/04/10',
      salary: '$237,500',
    },
    {
      name: 'Garrett Winters',
      position: 'Accountant',
      office: 'Tokyo',
      age: 63,
      startDate: '2011/07/25',
      salary: '$170,750',
    },
    {
      name: 'Ashton Cox',
      position: 'Junior Technical Author',
      office: 'San Francisco',
      age: 66,
      startDate: '2009/01/12',
      salary: '$86,000',
    },
    {
      name: 'Cedric Kelly',
      position: 'Senior Javascript Developer',
      office: 'Edinburgh',
      age: 22,
      startDate: '2012/03/29',
      salary: '$433,060',
    },
    {
      name: 'Airi Satou',
      position: 'Accountant',
      office: 'Tokyo',
      age: 33,
      startDate: '2008/11/28',
      salary: '$162,700',
    },
    {
      name: 'Brielle Williamson',
      position: 'Integration Specialist',
      office: 'New York',
      age: 61,
      startDate: '2012/12/02',
      salary: '$372,000',
    },
  ];
}

Fixed header

Use the fixedHeader option to ensure that a table's header is always visible while scrolling.

Name Position Office Age Start date Salary
Tiger NixonSystem ArchitectEdinburgh612011/04/25$320,800
Garrett WintersAccountantTokyo632011/07/25$170,750
Ashton CoxJunior Technical AuthorSan Francisco662009/01/12$86,000
Cedric KellySenior Javascript DeveloperEdinburgh222012/03/29$433,060
Airi SatouAccountantTokyo332008/11/28$162,700
Brielle WilliamsonIntegration SpecialistNew York612012/12/02$372,000
Herrod ChandlerSales AssistantSan Francisco592012/08/06$137,500
Rhona DavidsonIntegration SpecialistTokyo552010/10/14$327,900
Colleen HurstJavascript DeveloperSan Francisco392009/09/15$205,500
Sonya FrostSoftware EngineerEdinburgh232008/12/13$103,600

Rows per page:

10
1 - 10 of 14
<div class="datatable mt-4">
  <div class="datatable-inner" mdbScrollbar style="position: relative; max-height: 300px">
    <table
      class="table datatable-table"
      mdbTable
      mdbTableSort
      #table="mdbTable"
      #sort="mdbTableSort"
      [dataSource]="dataSource"
      [fixedHeader]="true"
      [sort]="sort"
      [pagination]="pagination"
    >
      <thead class="datatable-header">
        <tr>
          <th *ngFor="let header of headers" [mdbTableSortHeader]="header" scope="col">
            {{ header | titlecase }}
          </th>
        </tr>
      </thead>
      <tbody class="datatable-body">
        <tr *ngFor="let data of table.data" scope="row">
          <td>
            {{ data.name }}
          </td>
          <td>
            {{ data.position }}
          </td>
          <td>
            {{ data.office }}
          </td>
          <td>
            {{ data.age }}
          </td>
          <td>
            {{ data.startDate }}
          </td>
          <td>
            {{ data.salary }}
          </td>
        </tr>
      </tbody>
    </table>
  </div>
  <mdb-table-pagination #pagination></mdb-table-pagination>
</div>
import { Component } from '@angular/core';

export interface Person {
  name: string;
  position: string;
  office: string;
  age: number;
  startDate: string;
  salary: string;
}

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

  headers = ['Name', 'Position', 'Office', 'Age', 'Start Date', 'Salary'];

  dataSource: Person[] = [
    {
      name: 'Tiger Nixon',
      position: 'System Architect',
      office: 'Edinburgh',
      age: 61,
      startDate: '2011/04/25',
      salary: '$320,800',
    },
    {
      name: 'Sonya Frost',
      position: 'Software Engineer',
      office: 'Edinburgh',
      age: 23,
      startDate: '2008/12/13',
      salary: '$103,600',
    },
    {
      name: 'Jena Gaines',
      position: 'Office Manager',
      office: 'London',
      age: 30,
      startDate: '2008/12/19',
      salary: '$90,560',
    },
    {
      name: 'Quinn Flynn',
      position: 'Support Lead',
      office: 'Edinburgh',
      age: 22,
      startDate: '2013/03/03',
      salary: '$342,000',
    },
    {
      name: 'Charde Marshall',
      position: 'Regional Director',
      office: 'San Francisco',
      age: 36,
      startDate: '2008/10/16',
      salary: '$470,600',
    },
    {
      name: 'Haley Kennedy',
      position: 'Senior Marketing Designer',
      office: 'London',
      age: 43,
      startDate: '2012/12/18',
      salary: '$313,500',
    },
    {
      name: 'Tatyana Fitzpatrick',
      position: 'Regional Director',
      office: 'London',
      age: 19,
      startDate: '2010/03/17',
      salary: '$385,750',
    },
    {
      name: 'Michael Silva',
      position: 'Marketing Designer',
      office: 'London',
      age: 66,
      startDate: '2012/11/27',
      salary: '$198,500',
    },
    {
      name: 'Paul Byrd',
      position: 'Chief Financial Officer (CFO)',
      office: 'New York',
      age: 64,
      startDate: '2010/06/09',
      salary: '$725,000',
    },
    {
      name: 'Gloria Little',
      position: 'Systems Administrator',
      office: 'New York',
      age: 59,
      startDate: '2009/04/10',
      salary: '$237,500',
    },
    {
      name: 'Garrett Winters',
      position: 'Accountant',
      office: 'Tokyo',
      age: 63,
      startDate: '2011/07/25',
      salary: '$170,750',
    },
    {
      name: 'Ashton Cox',
      position: 'Junior Technical Author',
      office: 'San Francisco',
      age: 66,
      startDate: '2009/01/12',
      salary: '$86,000',
    },
    {
      name: 'Cedric Kelly',
      position: 'Senior Javascript Developer',
      office: 'Edinburgh',
      age: 22,
      startDate: '2012/03/29',
      salary: '$433,060',
    },
    {
      name: 'Airi Satou',
      position: 'Accountant',
      office: 'Tokyo',
      age: 33,
      startDate: '2008/11/28',
      salary: '$162,700',
    },
    {
      name: 'Brielle Williamson',
      position: 'Integration Specialist',
      office: 'New York',
      age: 61,
      startDate: '2012/12/02',
      salary: '$372,000',
    },
  ];
}

Async data

Loading content asynchronously is an important part of working with data tables - with MDB Datatable you can easily display content after fetching it from API.

Email Name Phone Username Website Address Company

Loading results...

Rows per page:

10
0 of 0
<div class="datatable mt-4">
  <div class="datatable-inner" mdbScrollbar style="position: relative">
    <table
      class="table datatable-table"
      mdbTable
      mdbTableSort
      #table="mdbTable"
      #sort="mdbTableSort"
      [dataSource]="dataSource$ | async"
      [pagination]="pagination"
      [sort]="sort"
    >
      <thead class="datatable-header">
        <tr>
          <th *ngFor="let header of headers" [mdbTableSortHeader]="header" scope="col">
            {{ header | titlecase }}
          </th>
        </tr>
      </thead>
      <tbody class="datatable-body" *ngIf="!loading">
        <tr *ngFor="let data of table.data" scope="row">
          <td>
            {{ data.email }}
          </td>
          <td>
            {{ data.name }}
          </td>
          <td>
            {{ data.phone }}
          </td>
          <td>
            {{ data.username }}
          </td>
          <td>
            {{ data.website }}
          </td>
          <td>{{ data.address.city }}, {{ data.address.street }}</td>
          <td>
            {{ data.company.name }}
          </td>
        </tr>
      </tbody>
    </table>
    <ng-container *ngIf="loading">
      <div class="datatable-loader bg-light">
        <span class="datatable-loader-inner">
          <span class="datatable-progress bg-primary"></span>
        </span>
      </div>
      <p class="text-center text-muted my-4">Loading results...</p>
    </ng-container>
  </div>
  <mdb-table-pagination #pagination></mdb-table-pagination>
</div>
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { delay, tap } from 'rxjs/operators';

export interface Person {
  name: string;
  position: string;
  office: string;
  age: number;
  startDate: string;
  salary: string;
}

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
})
export class AppComponent implements OnInit {
  dataUrl = 'https://jsonplaceholder.typicode.com/users';
  dataSource$: Observable<any[]>;
  loading = true;

  constructor(private _http: HttpClient) {}

  ngOnInit(): void {
    this.dataSource$ = this._http.get<any>(this.dataUrl).pipe(
      delay(7000),
      tap(() => (this.loading = false))
    );
  }

  headers = ['Name', 'Position', 'Office', 'Age', 'Start Date', 'Salary'];

  reloadData(): void {
    this.loading = true;
    this.dataSource$ = this._http.get<any>(this.dataUrl).pipe(
      delay(7000),
      tap(() => (this.loading = false))
    );
  }
}

Action buttons

With the Datatable it's possible to render custom content, such as action buttons and attach listeners to their events.

Name Position Office Contact
Tiger NixonSystem ArchitectEdinburgh
Sonya FrostSoftware EngineerEdinburgh
Tatyana FitzpatrickRegional DirectorLondon

Rows per page:

10
1 - 3 of 3
<div class="datatable mt-4">
  <table
    class="table datatable-table"
    mdbTable
    mdbTableSort
    #table="mdbTable"
    #sort="mdbTableSort"
    [dataSource]="dataSource"
    [hover]="true"
    [sort]="sort"
    [pagination]="pagination"
  >
    <thead class="datatable-header">
      <tr>
        <th *ngFor="let header of headers" [mdbTableSortHeader]="header" scope="col">
          {{ header | titlecase }}
        </th>
      </tr>
    </thead>
    <tbody class="datatable-body">
      <tr
        *ngFor="let data of table.data"
        scope="row"
      >
        <td>
          {{ data.name }}
        </td>
        <td>
          {{ data.position }}
        </td>
        <td>
          {{ data.office }}
        </td>
        <td>
          <button
            class="call-btn btn btn-outline-primary btn-floating btn-sm"
            (click)="logData(data.phone)">
            <i class="fa fa-phone"></i>
          </button>
          <button
            class="messhandle-btn btn ms-2 btn-primary btn-floating btn-sm"
            (click)="logData(data.email)">
            <i class="fa fa-envelope"></i>
          </button>
        </td>
      </tr>
    </tbody>
  </table>
  <mdb-table-pagination #pagination></mdb-table-pagination>
</div>
import { Component } from '@angular/core';

export interface Person {
  name: string;
  position: string;
  office: string;
  age: number;
  phone: string;
  email: string;
}

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

  headers = ['Name', 'Position', 'Office'];

  dataSource: Person[] = [
    {
      name: 'Tiger Nixon',
      position: 'System Architect',
      office: 'Edinburgh',
      phone: '+48000000000',
      email: 'tiger.nixon@gmail.com'
    },
    {
      name: 'Sonya Frost',
      position: 'Software Engineer',
      office: 'Edinburgh',
      phone: '+53456123456',
      email: 'sfrost@gmail.com'
    },
    {
      name: 'Tatyana Fitzpatrick',
      position: 'Regional Director',
      office: 'London',
      phone: '+42123432456',
      email: 'tfitz@gmail.com'
    }
  ];

  logData(data: string): void {
    console.log(data)
  }
}

Cell formatting

Use [ngStyle] attribute directive to implement cell formatting to color individual cells.

Product Quantity Purchases
Product 5104240
Product 489230
Product 94206
Product 850199
Product 697187
Product 7167130
Product 245110
Product 110103
Product 1122100
Product 1012088

Rows per page:

10
1 - 10 of 11
<div class="datatable mt-4">
  <table
    class="table datatable-table"
    mdbTable
    mdbTableSort
    #table="mdbTable"
    #sort="mdbTableSort"
    [dataSource]="dataSource"
    [pagination]="pagination"
    [sort]="sort"
  >
    <thead class="datatable-header">
      <tr>
        <th *ngFor="let header of headers" [mdbTableSortHeader]="header" scope="col">
          {{ header | titlecase }}
        </th>
      </tr>
    </thead>
    <tbody class="datatable-body">
      <tr *ngFor="let data of table.data" scope="row">
        <td>
          {{ data.product }}
        </td>
        <td>
          {{ data.quantity }}
        </td>
        <td [ngStyle]="formatCell(data.purchases)">
          {{ data.purchases }}
        </td>
      </tr>
    </tbody>
  </table>
  <mdb-table-pagination #pagination></mdb-table-pagination>
</div>
import { Component } from '@angular/core';

export interface Product {
  product: string;
  quantity: number;
  purchases: number;
}

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

  headers = ['Product', 'Quantity', 'Purchases'];

  dataSource: Product[] = [
  { product: 'Product 1', quantity: 10, purchases: 103 },
  { product: 'Product 2', quantity: 45, purchases: 110 },
  { product: 'Product 3', quantity: 76, purchases: 56 },
  { product: 'Product 4', quantity: 89, purchases: 230 },
  { product: 'Product 5', quantity: 104, purchases: 240 },
  { product: 'Product 6', quantity: 97, purchases: 187 },
  { product: 'Product 7', quantity: 167, purchases: 130 },
  { product: 'Product 8', quantity: 50, purchases: 199 },
  { product: 'Product 9', quantity: 4, purchases: 206 },
  { product: 'Product 10', quantity: 120, purchases: 88 },
  { product: 'Product 11', quantity: 22, purchases: 100 },
];

colors = ['#E3F2FD', '#BBDEFB', '#90CAF9', '#64B5F6', '#42A5F5'];
maxValue = Math.max(...this.dataSourceCellFormatting.map((data) => data.purchases));
minValue = Math.min(...this.dataSourceCellFormatting.map((data) => data.purchases));
step = (this.maxValue - this.minValue) / (this.colors.length - 1);

formatCell(value: number): { 'background-color': string; 'font-weight': number } {
  const colorIndex = Math.floor((value - this.minValue) / this.step);
  const backgroundColor = this.colors[colorIndex];
  const fontWeight = 400;

  return {
    'background-color': backgroundColor,
    'font-weight': fontWeight,
  };
}
}

Clickable rows

Click on the row to preview the message.

Selecting the row with checkbox doesn't trigger rowClick event.

Note: To prevent this action with other clickable elements within the row, call stopPropagation() method.

Note: This feature cannot be used simultaneously with edit option.

Actions From Title Message Date
admin@mdbootstrap.comMDBootstrap spring saleLorem ipsum dolor si...11/12/2019
user@mdbootstrap.comHow to purchase MDB5 package?Quisque tempor ligul...10/12/2019
user@mdbootstrap.comLicence renewalLorem ipsum dolor si...09/12/2019
admin@mdbootstrap.comBlack friday offerLorem ipsum dolor si...08/12/2019

Rows per page:

10
1 - 4 of 4
<div class="datatable mt-4">
  <table
    class="table datatable-table"
    mdbTable
    mdbTableSort
    #table="mdbTable"
    #sort="mdbTableSort"
    [dataSource]="dataSource"
    [sort]="sort"
    [pagination]="pagination"
  >
    <thead class="datatable-header">
      <tr>
        <th scope="col">
          <div class="form-check d-flex align-items-center mb-0">
            <input
              mdbCheckbox
              class="datatable-header-checkbox form-check-input"
              type="checkbox"
              [checked]="allRowsSelected()"
              (checkboxChange)="toggleAll($event)"
            />
          </div>
        </th>
        <th *ngFor="let header of headers" [mdbTableSortHeader]="header" scope="col">
          {{ header | titlecase }}
        </th>
      </tr>
    </thead>
    <tbody class="datatable-body">
      <tr
        *ngFor="let data of table.data"
        scope="row"
        (click)="onRowClick()"
        [class.active]="selections.has(data)"
        style="cursor: pointer;"
      >
        <td>
          <div class="form-check">
            <input
              mdbCheckbox
              (click)="$event.stopPropagation()"
              (checkboxChange)="toggleSelection($event, data)"
              [checked]="selections.has(data)"
              class="datatable-row-checkbox form-check-input"
              type="checkbox"
            />
          </div>
        </td>
        <td>
          {{ data.name }}
        </td>
        <td>
          {{ data.position }}
        </td>
        <td>
          {{ data.office }}
        </td>
        <td>
          {{ data.age }}
        </td>
        <td>
          {{ data.startDate }}
        </td>
        <td>
          {{ data.salary }}
        </td>
        <td>
          <button
            class="call-btn btn btn-outline-primary btn-floating btn-sm"
            (click)="$event.stopPropagation()"
          >
            <i class="fa fa-phone"></i>
          </button>
          <button
            class="messhandle-btn btn ms-2 btn-primary btn-floating btn-sm"
            (click)="$event.stopPropagation()"
          >
            <i class="fa fa-envelope"></i>
          </button>
        </td>
      </tr>
    </tbody>
  </table>
  <mdb-table-pagination #pagination></mdb-table-pagination>
</div>
import { Component } from '@angular/core';
import { MdbCheckboxChange } from 'mdb-angular-ui-kit/checkbox';

export interface Person {
  name: string;
  position: string;
  office: string;
  age: number;
  startDate: string;
  salary: string;
}

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

  selections = new Set<Person>();

  headers = ['Name', 'Position', 'Office', 'Age', 'Start Date', 'Salary'];

  dataSource: Person[] = [
    {
      name: 'Tiger Nixon',
      position: 'System Architect',
      office: 'Edinburgh',
      age: 61,
      startDate: '2011/04/25',
      salary: '$320,800',
    },
    {
      name: 'Sonya Frost',
      position: 'Software Engineer',
      office: 'Edinburgh',
      age: 23,
      startDate: '2008/12/13',
      salary: '$103,600',
    },
    {
      name: 'Jena Gaines',
      position: 'Office Manager',
      office: 'London',
      age: 30,
      startDate: '2008/12/19',
      salary: '$90,560',
    },
    {
      name: 'Quinn Flynn',
      position: 'Support Lead',
      office: 'Edinburgh',
      age: 22,
      startDate: '2013/03/03',
      salary: '$342,000',
    },
    {
      name: 'Charde Marshall',
      position: 'Regional Director',
      office: 'San Francisco',
      age: 36,
      startDate: '2008/10/16',
      salary: '$470,600',
    },
    {
      name: 'Haley Kennedy',
      position: 'Senior Marketing Designer',
      office: 'London',
      age: 43,
      startDate: '2012/12/18',
      salary: '$313,500',
    },
    {
      name: 'Tatyana Fitzpatrick',
      position: 'Regional Director',
      office: 'London',
      age: 19,
      startDate: '2010/03/17',
      salary: '$385,750',
    },
    {
      name: 'Michael Silva',
      position: 'Marketing Designer',
      office: 'London',
      age: 66,
      startDate: '2012/11/27',
      salary: '$198,500',
    },
    {
      name: 'Paul Byrd',
      position: 'Chief Financial Officer (CFO)',
      office: 'New York',
      age: 64,
      startDate: '2010/06/09',
      salary: '$725,000',
    },
    {
      name: 'Gloria Little',
      position: 'Systems Administrator',
      office: 'New York',
      age: 59,
      startDate: '2009/04/10',
      salary: '$237,500',
    },
    {
      name: 'Garrett Winters',
      position: 'Accountant',
      office: 'Tokyo',
      age: 63,
      startDate: '2011/07/25',
      salary: '$170,750',
    },
    {
      name: 'Ashton Cox',
      position: 'Junior Technical Author',
      office: 'San Francisco',
      age: 66,
      startDate: '2009/01/12',
      salary: '$86,000',
    },
    {
      name: 'Cedric Kelly',
      position: 'Senior Javascript Developer',
      office: 'Edinburgh',
      age: 22,
      startDate: '2012/03/29',
      salary: '$433,060',
    },
    {
      name: 'Airi Satou',
      position: 'Accountant',
      office: 'Tokyo',
      age: 33,
      startDate: '2008/11/28',
      salary: '$162,700',
    },
    {
      name: 'Brielle Williamson',
      position: 'Integration Specialist',
      office: 'New York',
      age: 61,
      startDate: '2012/12/02',
      salary: '$372,000',
    },
  ];

  onRowClick(): void {
    alert('row clicked');
  }

  allRowsSelected(): boolean {
    const selectionsLength = this.selections.size;
    const dataLength = this.dataSource.length;
    return selectionsLength === dataLength;
  }

  toggleSelection(event: MdbCheckboxChange, value: Person): void {
    if (event.checked) {
      this.select(value);
    } else {
      this.deselect(value);
    }
  }

  toggleAll(event: MdbCheckboxChange): void {
    if (event.checked) {
      this.dataSource.forEach((row: Person) => {
        this.select(row);
      });
    } else {
      this.dataSource.forEach((row: Person) => {
        this.deselect(row);
      });
    }
  }

  select(value: Person): void {
    if (!this.selections.has(value)) {
      this.selections.add(value);
    }
  }

  deselect(value: Person): void {
    if (this.selections.has(value)) {
      this.selections.delete(value);
    }
  }
}

Datatables - API


Import

import { MdbTableModule } from 'mdb-angular-ui-kit/table';
…
@NgModule ({
  ...
  imports: [MdbTableModule],
  ...
})

Inputs

MdbTableDirective

Name Type Default Description
bordered Boolean false Adds borders to a datatable
borderless Boolean false Removes all borders from a datatable
dataSource T[] [] Changes table data source array
fixedHeader Boolean false When it's set to true, the table's header will remain visible while scrolling
filterFn (data: T, searchTerm: string) => boolean - Changes function used for data search
hover Boolean false Changes the background color of a hovered row
pagination MdbTablePaginationComponent - Changes attached table pagination component
sort MdbTableSortDirective - Changes attached table sort directive
sm Boolean false Decreases a row's paddings
striped Boolean false Slightly changes the background's color in every other row

MdbTableSortHeaderDirective

Name Type Default Description
disableSort boolean false Disables sorting for chosen column
forceSort boolean false When it's set to true, the table's sort will toggle between two options: ascending and descending. The initial state will not be one of the options.
mdbTableSortHeader string '' Changes name of the sort header

MdbTablePaginationComponent

Name Type Default Description
allText string 'All' Defines text for 'All' option
disabled boolean false Whether pagination should be disabled
entries number 10 Changes number of displayed entries
entriesOptions Array [10, 25, 50, 200] Options available to choose from in a pagination select (rows per page)
nextButtonDisabled boolean false Whether next button should be disabled
page number 0 Sets current page
prevButtonDisabled boolean false Whether previous button should be disabled
rowsPerPageText string Rows per page Changes 'Rows per page' text value
ofText string of Changes 'of' text value
showAllEntries boolean false Defines whether 'All' option is available
total number 0 Changes number of total pagination entries

Outputs

MdbTableSortDirective

Name Type Description
sortChange EventEmitter<MdbSortChange> Event fired on data sort change

MdbTablePaginationComponent

Name Type Description
paginationChange EventEmitter<MdbPaginationChange> Event fired on pagination change
<div class="datatable mt-4">
  <table
    class="table datatable-table"
    mdbTable
    mdbTableSort
    #table="mdbTable"
    #sort="mdbTableSort"
    [dataSource]="dataSource"
    [pagination]="pagination"
    [sort]="sort"
    (sortChange)="onSortChange($event)"
  >
    <thead class="datatable-header">
      <tr>
        <th *ngFor="let header of headers" [mdbTableSortHeader]="header" scope="col">
          {{ header | titlecase }}
        </th>
      </tr>
    </thead>
    <tbody class="datatable-body">
      <tr *ngFor="let data of table.data" scope="row">
        <td>
          {{ data.name }}
        </td>
        <td>
          {{ data.position }}
        </td>
        <td>
          {{ data.office }}
        </td>
        <td>
          {{ data.age }}
        </td>
        <td>
          {{ data.startDate }}
        </td>
        <td>
          {{ data.salary }}
        </td>
      </tr>
    </tbody>
  </table>
  <mdb-table-pagination #pagination (paginationChange)="onPaginationChange($event)"></mdb-table-pagination>
</div>
import { Component } from '@angular/core';
import { MdbSortChange, MdbPaginationChange } from 'mdb-angular-ui-kit/table';

export interface Person {
  name: string;
  position: string;
  office: string;
  age: number;
  startDate: string;
  salary: string;
}

@Component({ selector: 'app-datatable',
  templateUrl: './datatable.component.html',
  styleUrls: ['./datatable.component.scss'],
})
export class DatatableComponent {
  headers = ['Name', 'Position', 'Office', 'Age', 'Start Date', 'Salary'];

  dataSource: Person[] = [
    {
      name: 'Tiger Nixon',
      position: 'System Architect',
      office: 'Edinburgh',
      age: 61,
      startDate: '2011/04/25',
      salary: '$320,800',
    },
    {
      name: 'Sonya Frost',
      position: 'Software Engineer',
      office: 'Edinburgh',
      age: 23,
      startDate: '2008/12/13',
      salary: '$103,600',
    },
    {
      name: 'Jena Gaines',
      position: 'Office Manager',
      office: 'London',
      age: 30,
      startDate: '2008/12/19',
      salary: '$90,560',
    },
    {
      name: 'Quinn Flynn',
      position: 'Support Lead',
      office: 'Edinburgh',
      age: 22,
      startDate: '2013/03/03',
      salary: '$342,000',
    },
    {
      name: 'Charde Marshall',
      position: 'Regional Director',
      office: 'San Francisco',
      age: 36,
      startDate: '2008/10/16',
      salary: '$470,600',
    },
    {
      name: 'Haley Kennedy',
      position: 'Senior Marketing Designer',
      office: 'London',
      age: 43,
      startDate: '2012/12/18',
      salary: '$313,500',
    },
    {
      name: 'Tatyana Fitzpatrick',
      position: 'Regional Director',
      office: 'London',
      age: 19,
      startDate: '2010/03/17',
      salary: '$385,750',
    },
    {
      name: 'Michael Silva',
      position: 'Marketing Designer',
      office: 'London',
      age: 66,
      startDate: '2012/11/27',
      salary: '$198,500',
    },
    {
      name: 'Paul Byrd',
      position: 'Chief Financial Officer (CFO)',
      office: 'New York',
      age: 64,
      startDate: '2010/06/09',
      salary: '$725,000',
    },
    {
      name: 'Gloria Little',
      position: 'Systems Administrator',
      office: 'New York',
      age: 59,
      startDate: '2009/04/10',
      salary: '$237,500',
    },
    {
      name: 'Garrett Winters',
      position: 'Accountant',
      office: 'Tokyo',
      age: 63,
      startDate: '2011/07/25',
      salary: '$170,750',
    },
    {
      name: 'Ashton Cox',
      position: 'Junior Technical Author',
      office: 'San Francisco',
      age: 66,
      startDate: '2009/01/12',
      salary: '$86,000',
    },
    {
      name: 'Cedric Kelly',
      position: 'Senior Javascript Developer',
      office: 'Edinburgh',
      age: 22,
      startDate: '2012/03/29',
      salary: '$433,060',
    },
    {
      name: 'Airi Satou',
      position: 'Accountant',
      office: 'Tokyo',
      age: 33,
      startDate: '2008/11/28',
      salary: '$162,700',
    },
    {
      name: 'Brielle Williamson',
      position: 'Integration Specialist',
      office: 'New York',
      age: 61,
      startDate: '2012/12/02',
      salary: '$372,000',
    },
  ];

  constructor() {}

  onSortChange(event: MdbSortChange): void {
    console.log('sort change event', event);
  }

  onPaginationChange(event: MdbPaginationChange): void {
    console.log('pagination change event', event);
  }
}

Advanced types

MdbPaginationChange

interface MdbPaginationChange {
  page: number;
  entries: number;
  total: number;
}

MdbSortChange

interface MdbSortChange {
  name: string;
  direction: MdbSortDirection;
}

MdbSortDirection

type MdbSortDirection = 'asc' | 'desc' | 'none';