Let’s DataTable

Olotin Temitope
6 min readAug 22, 2020

DataTable is a very powerful plugin that let you turn your data into a table format where you can filter, sort, search and paginate them. It does this for you out of the box, without you doing any further configuration.

The purpose of this tutorial is about improving performance when dealing with dataTable. As your data grows, letting DataTable load up all your records running into tens of thousands does not make much sense for the following reasons;

  • It increases page loading time which might discourage people from using your web application.
  • It might crash your web application because of having to load too much data at once.
  • Having too much information to feed the users might be a bit discouraging.
  • Adding custom filters/fields might be a bit difficult to implement.

How can we take control of dataTable and use it to our advantage? Well, follow me as I show you what you need to do.

The following steps should help you take control of DataTable to improve your website performance.

  1. Control what you load from the server
  2. Control the data formatting
  3. Control the pagination
  4. Control the re-rendering of the table
  5. Control the filter, sorting and searching

Control what you load from the server

This is where paginating your server response takes place, and you can decide to load a certain number of records per page. DataTable allows passing an ajax object in the configuration.

DataTable({
ajax: {
url: requestURL,
headers: {
'Authorization': 'Bearer ' + $('#token').val()
},
dataSrc: function (JSONResponseFromServer) {
return filterData(JSONResponseFromServer);
},
});

You can as well add headers to the ajax configuration depending on your use case. The “dataSrc” lets you format the JSON before they are loaded into the table.

Control the data formatting

Immediately the XHR response is returned to the DataTable, which calls a callback function.

initComplete: function (settings, JSONResponseFromServer) {
// buildPagination(JSONResponseFromServer);
// format JSON response
}

In this callback function, you can handle the data, and build your pagination. If you do not want to use the “initComplete” callback to format your response, you can the ajax “dataSrc” to handle the formatting for yourself.

DataTable allows you to format your table column which allows you to build links, add images, etc.

DataTable({
columnDefs: [
target: 0 // table column index
data: null // null if you're not sure of the column name
orderable: false, // if you do not want the column to be sorted
render: function(data, type, row) {
// You can do date formatting
return moment().format("l hh:ss a z");

//Decide what data format to return for the column
const complete = row.complete;
if (true === complete) {
return "Yes";
}
return "No";
}
] })

Control the pagination

Now that you’ve formatted your response, you need to build the pagination yourself since you’ve hijacked the control from DataTable.

The following example is how I used Laravel and JQuery to build my pagination. I think you should build yours in your preferred language or stack.

Sample Response from Laravel Controller

{
current_page: 1
data: [{id: 5696, user_id: 2319, name: "House Wallace LLC", address: "Necessitatibus lauda"}]
first_page_url: "http://byd.test/api/admin/users?page=1"
from: 1
last_page: 81
last_page_url: "http://byd.test/api/admin/users?page=81"
next_page_url: "http://byd.test/api/admin/users?page=2"
path: "http://byd.test/api/admin/users"
per_page: 50
prev_page_url: null
to: 50
total: 4040
}

JavaScript buildPagination function

const buildPagination = (JSONResponseFromServer) => {
const json = JSONResponseFromServer
let links = "";
const paginate = $("ul.pagination");
const nextPageURL = json.next_page_url;
const prevPageURL = json.prev_page_url;
if (null === prevPageURL) {
links += `
<li class="paginate_button previous disabled" id="nonprofits-list_previous">
<a href="#" aria-controls="nonprofits-list" data-dt-idx="0" tabindex="0">Previous</a>
</li>`;
}
if (null !== prevPageURL) {
links += `
<li class="paginate_button previous" id="list_previous">
<a href="#" data-dt-href="${json.prev_page_url}" aria-controls="list" data-dt-idx="${
json.current_page - 1
}" tabindex="0">Previous</a>
</li>`;
}
if (null !== json.last_page_url) {
links += `
<li class="paginate_button disabled pager_info">
<a href="#" aria-controls="nonprofits-list" data-dt-idx="1" tabindex="0" disabled>Page ${json.current_page} of ${json.last_page}</a>
</li>`;
}
if (null === nextPageURL) {
links += `
<li class="paginate_button next disabled" id="list_next">
<a href="#" aria-controls="list" data-dt-idx="0" tabindex="0">Next</a>
</li>`;
}
if (null !== nextPageURL) {
links += `
<li class="paginate_button next" id="list_next">
<a href="#" data-dt-href="${json.next_page_url}" aria-controls="list" data-dt-idx="${
json.current_page + 1
}" tabindex="0">Next</a>
</li>`;
}
paginate.html("").append(links);
};

The code above helped me control my pagination and know when to fetch the next record. You can also cache the last response instead of fetching the record every time the user clicks on the next and previous buttons.

initComplete: function (settings, json) {
state = {};
const immutableJson = Object.assign({}, json);
immutableJson.data = filterData(immutableJson);
state[immutableJson.first_page_url] = immutableJson;
buildPaginationLinks(immutableJson);
},

The above code help with caching and fetching of already fetched requests, which can also improve the performance and user experience.

You can control the next and previous button click events

// pagination
$(document).on("click", ".next", function () {
refreshDataTable(table, $(this), state);
return false;
});
$(document).on("click", ".previous", function (e) {
refreshDataTable(table, $(this), state);
return false;
});
$(document).on("click", ".pager_info", function (e) {
return false;
});

Control the re-rendering of the table

DataTable also allows you to control when and how to re-render the table.

const reDrawDataTable = (table, JSONResponse) => {
const filteredData = filterUserData(JSONResponse)
table.clear();
table.rows.add(filteredData).draw();
};
//You can also doconst table = DataTable({});
table.clear();
table.rows.add(JSONResponse).draw();

The above code lets you re-draw and re-render the table. This is useful for pagination, updating the table with new changes and so on.

Control the filter, sorting and searching

Now you need to tell DataTable to let go of the control. We need to unbind the event so that we can handle it ourselves.

// unbind sorting click event
$("th").unbind("click.DT");
$("th").bind("click.DT", function (e) {
e.stopPropagation();

let sort = "";
const column = e.target.cellIndex;
const sortBy = $(this).html().trim().toLocaleLowerCase();
if (!_this.hasClass("sorting_desc")) {
sort = "desc";
_this.removeClass("sorting_asc");
_this.addClass("sorting_desc");
} else {
sort = "asc";
_this.removeClass("sorting_desc");
_this.addClass("sorting_asc");
}
// Fetch and redraw the table
fetchAndReDrawTable(requestURL, table);
// tell DataTable to sort the column
table.order([[column, sort]]);
return false;
});

You can build the filtering by yourself depending on your use case. My use case is to allow the admin to filter the users by state and complete status. And this is how I handled it.

const getFormFilter = () => {
const form = $("form.admin-users");
const state = form.find("select#state");
const complete = form.find("select#complete");
return {
state,
complete,
};
};
$("#state").on("change", function () {
const requestURL = filterUsers();
fetchAndReDrawTable(requestURL, dataTable);
return false;
});
$("#complete").on("change", function () {
const requestURL = filterUsers();
fetchAndReDrawTable(requestURL, dataTable);

return false;
});

My Filter users function looks like the below code sample

const filterUsers = () => {
const paginate = $("ul.pagination");
paginate.html("....");
const { state, complete } = getFormFilter(); const URL = 'any-url';
let search = $("#search");
const requestURL = encodeURI(
`${URL} q=${search.val().trim()}&state=${state.val()}&complete=${complete.val()}`
);
return requestURL;
};

You can also build a search box on top of your table, control the search result and re-render the table with your results.

Additional Tips

info: false, // Disable the table information at the bottom of the tablebDestroy: true, // Allow for creating a new instance of the datatable everytime eventhough it's been created before.bPaginate: false, // I want to control the pagination myself
pageLength: 50, //
stateSave: true // this will allow the table to remember the last pagination link you last visited until you clear your cache or build a reset button functionalitydom: '<"toolbar">frtip', // Add a contain/div where you can attach your custom control to the table header such as filter form, download dropdown type, etc

Example of dom: ‘<” toolbar”>frtip’

$("div.toolbar").html(
'<div class="" style="margin-top: 20px; ">' +
'<button type="button" class="btn btn-primary" id="delete_all">Delete All</button></div>'
);

Thanks for your patience while reading this article. Do let me know if you have comments and improvements to the article.

--

--

Olotin Temitope

Software Developer @andela | Music Lover | Data Science Enthusiast.