Name | Description |
---|---|
public constructor()
|
Create a new instance of the Datatable class.
Parameters:
|
public reload()
|
Reload the table data from the Ajax data source.
Parameters:
Return:
|
public adjustColumns()
|
Adjust column layout. |
public filter()
|
Filter row by the specified string.
Parameters:
|
public getContainer()
|
Returns a table wrapper element.
Parameters:
Return:
|
public getFilterContainer()
|
Returns a table filter container element.
Parameters:
Return:
|
public createRow()
|
Create a row.
Parameters:
Return:
|
public deleteRow()
|
Delete row.
Parameters:
Return:
|
public updateRow()
|
Update row.
Parameters:
Return:
|
public getRowData()
|
Get single row or all rows of data
Parameters:
Return:
|
public getRowCount()
|
Get the number of rows.
Parameters:
Return:
|
public getRowNodes()
|
Get row HTML elements.
Return:
|
public getRowObject()
|
Get the DataTable API instance containing the selected rows.
Parameters:
Return:
|
public column()
|
Select the column found by a the column selector.
Parameters:
Return:
|
public clear()
|
Clear the table of all data.
Return:
|
protected ajaxErrorHook()
|
Hook function called when an error occurs in an Ajax request to retrieve table data that can be implemented in a subclass. This method receives an HTTP status code and an XMLHttpRequest object. Parameters:
|
Name | Description |
---|---|
public api: DataTables.Api
|
DataTables.Api instance. This is read-only. |
Name | Position | Office | Age | Start date | Salary |
---|---|---|---|---|---|
Tiger Nixon | System Architect | Edinburgh | 61 | 2011/04/25 | 320800 |
Garrett Winters | Accountant | Tokyo | 63 | 2011/07/25 | 170750 |
Ashton Cox | Junior Technical Author | San Francisco | 66 | 2009/01/12 | 86000 |
Cedric Kelly | Senior Javascript Developer | Edinburgh | 22 | 2012/03/29 | 433060 |
<!--begin::Wrapper-->
<div class="d-flex flex-stack flex-wrap mb-5">
<!--begin::Search-->
<div class="d-flex align-items-center position-relative my-1 mb-2 mb-md-0">
<!--begin::Svg Icon | path: icons/duotune/general/gen021.svg-->
<span class="svg-icon svg-icon-1 position-absolute ms-4">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect opacity="0.5" x="17.0365" y="15.1223" width="8.15546" height="2" rx="1" transform="rotate(45 17.0365 15.1223)" fill="currentColor" />
<path d="M11 19C6.55556 19 3 15.4444 3 11C3 6.55556 6.55556 3 11 3C15.4444 3 19 6.55556 19 11C19 15.4444 15.4444 19 11 19ZM11 5C7.53333 5 5 7.53333 5 11C5 14.4667 7.53333 17 11 17C14.4667 17 17 14.4667 17 11C17 7.53333 14.4667 5 11 5Z" fill="currentColor" />
</svg>
</span>
<!--end::Svg Icon-->
<input data-ref="basicTableKeyword" data-on-search-basic-table type="text" class="form-control form-control-solid w-300px ps-15" placeholder="Search by name" maxlength="70" />
</div>
<!--end::Search-->
</div>
<!--end::Wrapper-->
<!--begin::Datatable-->
<table data-ref="basicTable" class="table table-row-bordered gy-5">
<thead>
<tr class="text-start text-gray-700 fw-bold fs-7 gs-0">
<th>Name</th>
<th>Position</th>
<th>Office</th>
<th>Age</th>
<th>Start date</th>
<th>Salary</th>
</tr>
</thead>
<tbody>
<tr>
<td>Tiger Nixon</td>
<td>System Architect</td>
<td>Edinburgh</td>
<td>61</td>
<td>2011/04/25</td>
<td>320800</td>
</tr>
<tr>
<td>Garrett Winters</td>
<td>Accountant</td>
<td>Tokyo</td>
<td>63</td>
<td>2011/07/25</td>
<td>170750</td>
</tr>
<tr>
<td>Ashton Cox</td>
<td>Junior Technical Author</td>
<td>San Francisco</td>
<td>66</td>
<td>2009/01/12</td>
<td>86000</td>
</tr>
<tr>
<td>Cedric Kelly</td>
<td>Senior Javascript Developer</td>
<td>Edinburgh</td>
<td>22</td>
<td>2012/03/29</td>
<td>433060</td>
</tr>
</tbody>
</table>
<!--end::Datatable-->
import {components} from 'metronic-extension';
function initBasicTable() {
return new components.Datatable(ref.basicTable, {
columnDefs: [
{targets: 0, data: 'name', name: 'name'},
{targets: 1, data: 'position', name: 'position'},
{targets: 2, data: 'office', name: 'office'},
{targets: 3, data: 'age', name: 'age'},
{targets: 4, data: 'startDate', name: 'startDate'},
{targets: 5, data: 'salary', name: 'salary'},
],
pageLength: 3
});
}
function initBasicTableSearchForm() {
$('body').on('input', '[data-on-search-basic-table]', () => {
// Filter data by keyword.
basicTable.filter('name:name', ref.basicTableKeyword.val());
});
}
// Search for elements.
const ref = components.selectRef();
// Initialize DataTable.
const basicTable = initBasicTable();
// Initialize events, etc.
initBasicTableSearchForm();
Name | Position | Office | Age | Start date | Salary |
---|
<!--begin::Wrapper-->
<div class="d-flex flex-stack flex-wrap mb-5">
<!--begin::Search-->
<div class="d-flex align-items-center position-relative my-1 mb-2 mb-md-0">
<!--begin::Svg Icon | path: icons/duotune/general/gen021.svg-->
<span class="svg-icon svg-icon-1 position-absolute ms-4">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect opacity="0.5" x="17.0365" y="15.1223" width="8.15546" height="2" rx="1" transform="rotate(45 17.0365 15.1223)" fill="currentColor" />
<path d="M11 19C6.55556 19 3 15.4444 3 11C3 6.55556 6.55556 3 11 3C15.4444 3 19 6.55556 19 11C19 15.4444 15.4444 19 11 19ZM11 5C7.53333 5 5 7.53333 5 11C5 14.4667 7.53333 17 11 17C14.4667 17 17 14.4667 17 11C17 7.53333 14.4667 5 11 5Z" fill="currentColor" />
</svg>
</span>
<!--end::Svg Icon-->
<input data-ref="serverSideProcessingTableKeyword" data-on-search-server-side-processing-table type="text" class="form-control form-control-solid w-300px ps-15" placeholder="Search by name" maxlength="70" />
</div>
<!--end::Search-->
</div>
<!--end::Wrapper-->
<!--begin::Datatable-->
<table data-ref="serverSideProcessingTable" class="table table-row-bordered gy-5">
<thead>
<tr class="text-start text-gray-700 fw-bold fs-7 gs-0">
<th>Name</th>
<th>Position</th>
<th>Office</th>
<th>Age</th>
<th>Start date</th>
<th>Salary</th>
</tr>
</thead>
<tbody></tbody>
</table>
<!--end::Datatable-->
import {components} from 'metronic-extension';
function initServerSideProcessingTable() {
return new components.Datatable(ref.serverSideProcessingTable, {
ajax: {
url: '/api/persons/pages',
data: d => {
d.search = {keyword: ref.serverSideProcessingTableKeyword.val()};
}
},
columnDefs: [
{targets: 0, data: 'name'},
{targets: 1, data: 'position'},
{targets: 2, data: 'office'},
{targets: 3, data: 'age'},
{targets: 4, data: 'startDate'},
{targets: 5, data: 'salary'},
],
pageLength: 3
});
}
function initServerSideProcessingTableSearchForm() {
$('body').on('input', '[data-on-search-server-side-processing-table]', () => {
// Reload when the filter is changed.
// The filter information is set in the parameters sent to the server from the ajax.data optional function.
serverSideProcessingTable.reload();
})
}
// Search for elements.
const ref = components.selectRef();
// Initialize DataTable.
const serverSideProcessingTable = initServerSideProcessingTable();
// Initialize events, etc.
initServerSideProcessingTableSearchForm();
{
"data": [
{
"id": 5,
"name": "Airi Satou",
"position": "Accountant",
"office": "Tokyo",
"age": 33,
"startDate": "2008-11-28",
"salary": 162700
},
{
"id": 25,
"name": "Angelica Ramos",
"position": "Chief Executive Officer (CEO)",
"office": "London",
"age": 47,
"startDate": "2009-10-09",
"salary": 1200000
},
{
"id": 3,
"name": "Ashton Cox",
"position": "Junior Technical Author",
"office": "San Francisco",
"age": 66,
"startDate": "2009-01-12",
"salary": 86000
}
],
"recordsTotal": 57,
"recordsFiltered": 57,
"draw": "1"
}
const router = require('express').Router();
const {query, validationResult} = require('express-validator');
const PersonModel = require('../../models/PersonModel');
// Get person page data.
router.get('/pages', [
// Validate parameters.
query('start').not().isEmpty().isInt({min: 0}),
query('length').not().isEmpty().isInt({min: 1}),
query('order').not().isEmpty().isIn(['name', 'position', 'office', 'age', 'startDate', 'salary']),
query('dir').not().isEmpty().isIn(['asc', 'desc']),
query('search.keyword').trim().optional({nullable: true, checkFalsy: true}).isLength({max: 70}),
query('draw').not().isEmpty().isInt({min: 1}),
], async (req, res) => {
// Check validation results.
const result = validationResult(req);
if (!result.isEmpty())
// If the parameter is invalid, a 400 error is returned.
return void res.status(400).end();
// Get page data.
const paginate = await PersonModel.paginate(req.query);
// Set the received drawing count as-is in the response.
paginate.draw = req.query.draw;
res.json(paginate);
});
module.exports = router;
const Model = require('express-sweet').database.Model;
const {merge} = require('deep-fusion');
module.exports = class extends Model {
static get table() {
return 'person';
}
static get attributes() {
return {
id: {
type: this.DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: this.DataTypes.STRING,
position: this.DataTypes.STRING,
office: this.DataTypes.STRING,
age: this.DataTypes.INTEGER,
startDate: this.DataTypes.DATE,
salary: this.DataTypes.DECIMAL(10, 2),
};
}
/**
* Get page data.
*/
static async paginate(options) {
// Initialize options.
options = merge({
start: 0,
length: 30,
order: 'name',
dir: 'asc',
search: {
keyword: null,
},
}, options);
// Filter conditions.
const where = {};
if (options.search.keyword)
where.name = {[super.Op.like]: `%${options.search.keyword}%`};
// Get page data.
const data = await super.findAll({
where,
offset: parseInt(options.start, 10),
limit: parseInt(options.length, 10),
order: [super.literal(`${options.order} ${options.dir}`)],
raw: true,
});
// Get the total number of records.
const recordsTotal = await super.count();
// Get the number of filtered records.
const recordsFiltered = await super.count({where});
return {data, recordsTotal, recordsFiltered};
}
}
Name | Position | Office | Age | Start date | Salary |
---|---|---|---|---|---|
Tiger Nixon | System Architect | Edinburgh | 61 | 2011/04/25 | 320800 |
Garrett Winters | Accountant | Tokyo | 63 | 2011/07/25 | 170750 |
Ashton Cox | Junior Technical Author | San Francisco | 66 | 2009/01/12 | 86000 |
Cedric Kelly | Senior Javascript Developer | Edinburgh | 22 | 2012/03/29 | 433060 |
<!--begin::Datatable-->
<table data-ref="columnVisibilityTable" class="table table-row-bordered gy-5">
<thead>
<tr class="text-start text-gray-700 fw-bold fs-7 gs-0">
<th>Name</th>
<th>Position</th>
<th>Office</th>
<th>Age</th>
<th>Start date</th>
<th>Salary</th>
</tr>
</thead>
<tbody>
<tr>
<td>Tiger Nixon</td>
<td>System Architect</td>
<td>Edinburgh</td>
<td>61</td>
<td>2011/04/25</td>
<td>320800</td>
</tr>
<tr>
<td>Garrett Winters</td>
<td>Accountant</td>
<td>Tokyo</td>
<td>63</td>
<td>2011/07/25</td>
<td>170750</td>
</tr>
<tr>
<td>Ashton Cox</td>
<td>Junior Technical Author</td>
<td>San Francisco</td>
<td>66</td>
<td>2009/01/12</td>
<td>86000</td>
</tr>
<tr>
<td>Cedric Kelly</td>
<td>Senior Javascript Developer</td>
<td>Edinburgh</td>
<td>22</td>
<td>2012/03/29</td>
<td>433060</td>
</tr>
</tbody>
</table>
<!--end::Datatable-->
import {components} from 'metronic-extension';
function initColumnVisibilityTable() {
return new components.Datatable(ref.columnVisibilityTable, {
dom: `<'row align-items-center'<'col-auto'B><'col dataTables_pager'p>><'row'<'col-12'tr>><'row'<'col-12 dataTables_pager'p>>`,
columnDefs: [
{targets: 0, data: 'name'},
{targets: 1, data: 'position'},
{targets: 2, data: 'office'},
{targets: 3, data: 'age'},
{targets: 4, data: 'startDate'},
{targets: 5, data: 'salary'},
],
pageLength: 3,
buttons: [
// Button to toggle column visibility.
{
extend: 'colvis',
text: 'Show / hide columns',
// Columns selector that defines the columns to include in the column visibility button set.
// CSS selectors, column indexes, etc. can be used to specify columns to switch visibility.
columns: ':eq(1),:eq(2),:eq(3),:eq(4),:eq(5)',
// columns: [1,2,3,4,5],
}
],
// Save the column visibility in the browser.
stateSave: true,
/* Enable/Disable saving for various datatables elements. Delete any items you do not wish to save.
- data.columns.search: This option allows for the saving of the search applied to individual columns to be enabled or disabled.
- data.columns.visible: This option allows for the saving of the visibility of the columns to be enabled or disabled.
- data.length: This option allows for the saving of the page length to be enabled or disabled.
- data.order: This option allows for the saving of the tables column sorting to be enabled or disabled.
- data.paging: This option allows for the saving of the paging to be enabled or disabled.
- data.scroller: This option allows for the saving of the scroller position to be enabled or disabled.
- data.search: This option allows for the saving of the search to be enabled or disabled.
- data.searchBuilder: This option allows for the saving of the searchBuilder state to be enabled or disabled.
- data.searchPanes: This option allows for the saving of the searchPanes state to be enabled or- disabled.
- data.select: This option allows for the saving of the select state to be enabled or disabled.
*/
stateSaveParams: (_, data) => {
delete data.columns.search;
// delete data.columns.visible;
delete data.length;
delete data.order;
delete data.paging;
delete data.scroller;
delete data.search;
delete data.searchBuilder;
delete data.searchPanes;
delete data.select;
}
});
}
// Search for elements.
const ref = components.selectRef();
// Initialize DataTable.
const columnVisibilityTable = initColumnVisibilityTable();
Name | Position | Office | Age | Start date | Salary |
---|---|---|---|---|---|
Tiger Nixon | System Architect | Edinburgh | 61 | 2011/04/25 | 320800 |
Garrett Winters | Accountant | Tokyo | 63 | 2011/07/25 | 170750 |
Ashton Cox | Junior Technical Author | San Francisco | 66 | 2009/01/12 | 86000 |
Cedric Kelly | Senior Javascript Developer | Edinburgh | 22 | 2012/03/29 | 433060 |
<!--begin::Datatable-->
<table data-ref="columnVisibilityWithIconButtonTable" class="table table-row-bordered gy-5">
<thead>
<tr class="text-start text-gray-700 fw-bold fs-7 gs-0">
<th>Name</th>
<th>Position</th>
<th>Office</th>
<th>Age</th>
<th>Start date</th>
<th>Salary</th>
</tr>
</thead>
<tbody>
<tr>
<td>Tiger Nixon</td>
<td>System Architect</td>
<td>Edinburgh</td>
<td>61</td>
<td>2011/04/25</td>
<td>320800</td>
</tr>
<tr>
<td>Garrett Winters</td>
<td>Accountant</td>
<td>Tokyo</td>
<td>63</td>
<td>2011/07/25</td>
<td>170750</td>
</tr>
<tr>
<td>Ashton Cox</td>
<td>Junior Technical Author</td>
<td>San Francisco</td>
<td>66</td>
<td>2009/01/12</td>
<td>86000</td>
</tr>
<tr>
<td>Cedric Kelly</td>
<td>Senior Javascript Developer</td>
<td>Edinburgh</td>
<td>22</td>
<td>2012/03/29</td>
<td>433060</td>
</tr>
</tbody>
</table>
<!--end::Datatable-->
import {components} from 'metronic-extension';
function initColumnVisibilityWithIconButtonTable() {
return new components.Datatable(ref.columnVisibilityWithIconButtonTable, {
dom: `<'row align-items-center'<'col dataTables_pager'p><'col-auto'B>><'row'<'col-12'tr>><'row'<'col-12 dataTables_pager'p>>`,
columnDefs: [
{targets: 0, data: 'name'},
{targets: 1, data: 'position'},
{targets: 2, data: 'office'},
{targets: 3, data: 'age'},
{targets: 4, data: 'startDate'},
{targets: 5, data: 'salary'},
],
pageLength: 3,
buttons: {
dom: {
button: {
// Disable the default button class (btn btn-secondary) to set the icon button (btn-icon).
className: null,
},
buttonLiner: {
// By default, a span element is added within the button, so disable it.
tag: null,
}
},
buttons: [
// Button to toggle column visibility.
{
extend: 'colvis',
columns: ':eq(1),:eq(2),:eq(3),:eq(4),:eq(5)',
text: '<i class="ki-solid ki-gear fs-1"></i>',
// Icon button style. Switch between them as you wish.
// className: 'btn btn-sm btn-icon btn-color-primary btn-active-light-primary',
className: 'btn btn-icon btn-color-gray-500 btn-active-color-primary',
// Display column drop-downs inside the table container. If this is not set, the column dropdowns will overflow the table container.
align: 'container',
}
],
},
// Save the column visibility in the browser.
stateSave: true,
/* Enable/Disable saving for various datatables elements. Delete any items you do not wish to save.
- data.columns.search: This option allows for the saving of the search applied to individual columns to be enabled or disabled.
- data.columns.visible: This option allows for the saving of the visibility of the columns to be enabled or disabled.
- data.length: This option allows for the saving of the page length to be enabled or disabled.
- data.order: This option allows for the saving of the tables column sorting to be enabled or disabled.
- data.paging: This option allows for the saving of the paging to be enabled or disabled.
- data.scroller: This option allows for the saving of the scroller position to be enabled or disabled.
- data.search: This option allows for the saving of the search to be enabled or disabled.
- data.searchBuilder: This option allows for the saving of the searchBuilder state to be enabled or disabled.
- data.searchPanes: This option allows for the saving of the searchPanes state to be enabled or- disabled.
- data.select: This option allows for the saving of the select state to be enabled or disabled.
*/
stateSaveParams: (_, data) => {
delete data.columns.search;
// delete data.columns.visible;
delete data.length;
delete data.order;
delete data.paging;
delete data.scroller;
delete data.search;
delete data.searchBuilder;
delete data.searchPanes;
delete data.select;
}
});
}
// Search for elements.
const ref = components.selectRef();
// Initialize DataTable.
const columnVisibilityWithIconButtonTable = initColumnVisibilityWithIconButtonTable();
firstAjax
option to false in the DataTable
constructor disables the initial Ajax call.
Name | Position | Office | Age | Start date | Salary |
---|
<!--begin::Wrapper-->
<div class="d-flex flex-stack flex-wrap mb-5">
<!--begin::Toolbar-->
<div class="d-flex justify-content-end">
<button data-on-load-disable-first-ajax-call-table type="button" class="btn btn-primary">Call Ajax to load data</button>
</div>
<!--end::Toolbar-->
</div>
<!--end::Wrapper-->
<!--begin::Datatable-->
<table data-ref="disableFirstAjaxCallTable" class="table table-row-bordered gy-5">
<thead>
<tr class="text-start text-gray-700 fw-bold fs-7 gs-0">
<th>Name</th>
<th>Position</th>
<th>Office</th>
<th>Age</th>
<th>Start date</th>
<th>Salary</th>
</tr>
</thead>
<tbody></tbody>
</table>
<!--end::Datatable-->
import {components} from 'metronic-extension';
function initDisableFirstAjaxCallTable() {
return new components.Datatable(ref.disableFirstAjaxCallTable, {
firstAjax: false,
ajax: '/api/persons/pages',
columnDefs: [
{targets: 0, data: 'name'},
{targets: 1, data: 'position'},
{targets: 2, data: 'office'},
{targets: 3, data: 'age'},
{targets: 4, data: 'startDate'},
{targets: 5, data: 'salary'},
],
pageLength: 3
});
}
function initDisableFirstAjaxCallTableForm() {
$('body').on('click', '[data-on-load-disable-first-ajax-call-table]', () => disableFirstAjaxCallTable.reload());
}
// Search for elements.
const ref = components.selectRef();
// Initialize DataTable.
const disableFirstAjaxCallTable = initDisableFirstAjaxCallTable();
// Initialize events, etc.
initDisableFirstAjaxCallTableForm();
const router = require('express').Router();
const {query, validationResult} = require('express-validator');
const PersonModel = require('../../models/PersonModel');
// Get person page data.
router.get('/pages', [
// Validate parameters.
query('start').not().isEmpty().isInt({min: 0}),
query('length').not().isEmpty().isInt({min: 1}),
query('order').not().isEmpty().isIn(['name', 'position', 'office', 'age', 'startDate', 'salary']),
query('dir').not().isEmpty().isIn(['asc', 'desc']),
query('search.keyword').trim().optional({nullable: true, checkFalsy: true}).isLength({max: 70}),
query('draw').not().isEmpty().isInt({min: 1}),
], async (req, res) => {
// Check validation results.
const result = validationResult(req);
if (!result.isEmpty())
// If the parameter is invalid, a 400 error is returned.
return void res.status(400).end();
// Get page data.
const paginate = await PersonModel.paginate(req.query);
// Set the received drawing count as-is in the response.
paginate.draw = req.query.draw;
res.json(paginate);
});
module.exports = router;
const Model = require('express-sweet').database.Model;
const {merge} = require('deep-fusion');
module.exports = class extends Model {
static get table() {
return 'person';
}
static get attributes() {
return {
id: {
type: this.DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: this.DataTypes.STRING,
position: this.DataTypes.STRING,
office: this.DataTypes.STRING,
age: this.DataTypes.INTEGER,
startDate: this.DataTypes.DATE,
salary: this.DataTypes.DECIMAL(10, 2),
};
}
/**
* Get page data.
*/
static async paginate(options) {
// Initialize options.
options = merge({
start: 0,
length: 30,
order: 'name',
dir: 'asc',
search: {
keyword: null,
},
}, options);
// Filter conditions.
const where = {};
if (options.search.keyword)
where.name = {[super.Op.like]: `%${options.search.keyword}%`};
// Get page data.
const data = await super.findAll({
where,
offset: parseInt(options.start, 10),
limit: parseInt(options.length, 10),
order: [super.literal(`${options.order} ${options.dir}`)],
raw: true,
});
// Get the total number of records.
const recordsTotal = await super.count();
// Get the number of filtered records.
const recordsFiltered = await super.count({where});
return {data, recordsTotal, recordsFiltered};
}
}