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 Nixon | System Architect | Edinburgh | 61 | 2011/04/25 | $320,800 |
Sonya Frost | Software Engineer | Edinburgh | 23 | 2008/12/13 | $103,600 |
Jena Gaines | Office Manager | London | 30 | 2008/12/19 | $90,560 |
Quinn Flynn | Support Lead | Edinburgh | 22 | 2013/03/03 | $342,000 |
Charde Marshall | Regional Director | San Francisco | 36 | 2008/10/16 | $470,600 |
Haley Kennedy | Senior Marketing Designert | London | 43 | 2012/12/18 | $313,500 |
Tatyana Fitzpatrick | Regional Director | London | 19 | 2010/03/17 | $385,750 |
Michael Silva | Marketing Designer | London | 66 | 2012/11/27 | $198,500 |
Paul Byrd | Chief Financial Officer (CFO) | New York | 64 | 2010/06/09 | $725,000 |
Gloria Little | Systems Administrator | New York | 59 | 2009/04/10 | $237,500 |
Garrett Winters | Accountant | Tokyo | 63 | 2011/07/25 | $170,750 |
Quinn Flynn | Support Lead | Edinburgh | 22 | 2013/03/03 | $342,000 |
Ashton Cox | Junior Technical Author | San Francisco | 66 | 2009/01/12 | $86,000 |
Cedric Kelly | Senior Javascript Developer | Edinburgh | 22 | 2012/03/29 | $433,060 |
Airi Satou | Accountant | Tokyo | 33 | 2008/11/28 | $162,700 |
Brielle Williamson | Integration Specialist | New York | 61 | 2012/12/02 | $372,000 |
<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',
},
];
}
Search
The search field is not a part of the Datatable - place an input field on your page and use
.search()
method to filter entries.
<mdb-form-control>
<input
mdbInput
type="text"
class="form-control"
id="search-input"
(keyup)="search($event)"
/>
<label mdbLabel class="form-label" for="search-input">Search</label>
</mdb-form-control>
<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 *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, ViewChild } from '@angular/core';
import { MdbTableDirective } from 'mdb-angular-ui-kit/table';
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 {
@ViewChild('table') table!: MdbTableDirective<Person>;
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',
},
];
search(event: Event): void {
const searchTerm = (event.target as HTMLInputElement).value;
this.table.search(searchTerm);
}
}
Advanced search
When using the searching method, you can specify which columns it should take under consideration - pass as a second argument a field (or array of fields). By default, searching will apply to all columns.
<div class="input-group mb-4">
<input
type="text"
id="advanced-search"
class="form-control"
id="search-input"
#searchInput
placeholder="phrase in:field1,field2"
/>
<button
class="btn btn-primary ripple-surface"
id="advanced-search-button"
type="button"
(click)="advancedSearch(searchInput.value)"
>
<i class="fa fa-search"></i>
</button>
</div>
<div class="datatable mt-4">
<table
class="table datatable-table"
mdbTable
mdbTableSort
#table="mdbTable"
#sort="mdbTableSort"
[dataSource]="dataSource"
[sort]="sort"
[pagination]="pagination"
[filterFn]="filterFn"
>
<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, ViewChild } from '@angular/core';
import { MdbTableDirective } from 'mdb-angular-ui-kit/table';
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 {
@ViewChild('table') table!: MdbTableDirective<Person>;
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',
},
];
advancedSearch(value: string): void {
this.table.search(value);
}
filterFn(data: any, searchTerm: string): boolean {
let [phrase, columns] = searchTerm.split(' in:').map((str) => str.trim());
return Object.keys(data).some((key: any) => {
if (columns?.length) {
let result;
columns.split(',').forEach((column) => {
if (
column.toLowerCase().trim() === key.toLowerCase() &&
data[key].toLowerCase().includes(phrase.toLowerCase())
) {
result = true;
}
});
return result;
}
if (data[key] && !columns?.length) {
return JSON.stringify(data).toLowerCase().includes(phrase.toLowerCase());
}
});
}
}
Selectable rows
You can easily build datatable with selectable rows.
<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.
<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.
<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.
<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.
<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.
<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.
<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';