Build a realtime table with DataTables
This tutorial uses jQuery and Bootstrap.
If you are building an app that handles extensive data, you might want to implement realtime tables at some point. Let’s take a content management system for instance. Large amounts of data are added and removed often, and we would like the changes to be available to consumers immediately.
In this tutorial, we are going to walk through implementing similar features on our realtime table. We will be using a few developer tools, which are:
-
jQuery: A small JavaScript library rich in features that enable easier DOM manipulation, event handling, animation and AJAX implementations.
-
Pusher Channels: A free realtime, easy to use pub/sub service. Channels makes realtime as easy as using basic events.
-
DataTables: A jQuery plug-in that employs progressive enhancement concepts to add advanced interaction controls to any HTML table.
-
Bootstrap: A front end framework for developing responsive, mobile first projects on the web
Here is a glimpse of what we are going to build:
Setting up DataTables
Running DataTables on our app is quite simple and straightforward. All we need to do is include the DataTables JavaScript file and the DataTables CSS file in our HTML page. There are a host of other plug-ins we can add to enhance editing abilities and extend the feature set of DataTables but basically we shall stick to these two files. It must be noted that being a jQuery plug-in, DataTables will rely on jQuery in order to work. To include DataTables on our page, we simply include the following links on our HTML page:
<head>
<link rel="stylesheet" type="text/css" href="//cdn.datatables.net/1.10.15/css/jquery.dataTables.css">
</head>
These links will be at the end of our <body>
element, just before its closing tag:
<body>
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script type="text/javascript" charset="utf8" src="//cdn.datatables.net/1.10.15/js/jquery.dataTables.js"></script>
<script src="https://cdn.datatables.net/plug-ins/1.10.15/api/row().show().js"></script>
</body>
Creating our table blueprint
To create our table blueprint, we insert a table
element in our page and, with jQuery, append the DataTable
method to it. This will initialize all the built-in features of DataTables. The DataTable
method takes an object as argument. The object has a data
property which takes [dataSet](https://github.com/christiannwamba/pusher-realtime-jquery-datatable/blob/master/data.js)
, an array of data that we intend to display on the table. We also include another property called columns
and set its value to an array of objects with each object’s value serving as a column header for our table.
const dataTable = $('#realtime').DataTable({
data: dataSet,
columns: [
{ title: 'Name' },
{ title: 'Position' },
{ title: 'Office' },
{ title: 'Extn.' },
{ title: 'Start date' },
{ title: 'Salary' }
]
});
The data set is stored in a different JavaScript file and should be imported before the above custom JS file:
<script src="data.js"></script>
<script src="script.js"></script>
Adding new records to the table
To add new records to our table, we first create a form with valid options in our HTML page. We then proceed to create a method called buildForm()
in our JavaScript file. Using jQuery, we make buildForm()
return the value of every option in our form below.
<div class="col-md-4 col-md-offset-1">
<h3 class="text-center">Create New Employee</h3>
<div class="form-group">
<label for="name">Name</label>
<input type="text" name="name" id="name" placeholder="Name" class="form-control">
</div>
<div class="form-group">
<label for="position">Position</label>
<select name="position" id="position" class="form-control">
<option value="">--Select Position--</option>
<option value="Frontend Developer">Frontend Developer</option>
<option value="UI/UX Engineer">UI/UX Engineer</option>
<option value="iOS Engineer">iOS Engineer</option>
<option value="Android Developer">Android Developer</option>
</select>
</div>
<div class="form-group">
<label for="office">Office</label>
<select name="office" id="office" class="form-control">
<option value="">--Select Office--</option>
<option value="Lagos">Lagos</option>
<option value="London">London</option>
<option value="New York">New York</option>
<option value="Berlin">Berlin</option>
</select>
</div>
<div class="form-group">
<label for="extn">Extn</label>
<input type="number" name="extn" id="extn" placeholder="Extn" class="form-control">
</div>
<div class="form-group">
<label for="startDate">Start Date</label>
<input type="date" name="startDate" id="startDate" placeholder="Start Date" class="form-control">
</div>
<div class="form-group">
<label for="salary">Salary</label>
<input type="number" name="salary" id="salary" placeholder="Salary" class="form-control">
</div>
<div class="form-group">
<button class="btn btn-info" id="add">Add</button>
</div>
</div>
We then proceed to create our buildForm()
method:
buildForm() {
return [
$('#name').val(),
$('#position').val(),
$('#office').val(),
$('#extn').val(),
$('#startDate')
.val()
.replace(new RegExp('-', 'g'), '/'),
`$${$('#salary').val()}`
];
},
We create a method called addRow()
to append whatever data buildForm()
returns.
addRow(dataTable) {
const formData = this.buildForm();
const addedRow = dataTable.row.add(formData).draw();
addedRow.show().draw(false);
const addedRowNode = addedRow.node();
console.log(addedRowNode);
$(addedRowNode).addClass('highlight');
}
The methods row.add()
and .draw()
are inbuilt DataTables API methods, other DataTables methods implemented in addRow()
are .show()
, .draw(false)
and .node()
:
row.add()
adds a new row to the table using the given data..draw()
redraws and updates the table in the current context..show()
displays a field in our table. This is useful for cases when you want to have extra form fields available, but only show them under certain conditions..draw(false)
adds a new row without resetting or distorting the current page..node()
serves as an event listener, it returns the DOM element for the requested field thus enabling DOM manipulation of the field.
We then take our addRow()
method which we built and bind it to a button using jQuery’s .click()
method. When the button is clicked, addRow()
automatically executes its functions on our table.
$('#add').on('click', this.addRow.bind(this, dataTable));
Selecting and removing existing records from table
Let’s now create a method called selectRow()
, its function is to select a row in our table. Selecting a row puts the row to the spot so we can be able to remove it. The method just adds a selected
class to the selected row and removes any other row that selected
class was previously added to:
selectRow(dataTable) {
if ($(this).hasClass('selected')) {
$(this).removeClass('selected');
} else {
dataTable.$('tr.selected').removeClass('selected');
$(this).addClass('selected');
}
}
We also create a method called removeRow()
, its function is to remove a row from our table. The row removed is the row with the selected
class:
removeRow(dataTable) {
dataTable.row('.selected').remove().draw( false );
}
We then proceed to bind selectRow()
and removeRow()
to their respective event triggers using jQuery’s .click()
method as we did previously with addRow()
.
const self = this;
$('#realtime tbody').on('click', 'tr', function(){
self.selectRow.bind(this, dataTable)();
});
$('#remove').on('click', this.removeRow.bind(this, dataTable));
Realtime updates with Pusher
To enable realtime updates on our table, we will integrate Pusher Channels. Channels is a simple hosted API for quickly, easily and securely implementing realtime two-way functionality on web and mobile apps. To achieve this, Pusher is to be installed both on the client side and on the server side. The client side is with the <script>
tag while we npm install on the server side. A couple of frameworks and packages will be integrated alongside Pusher, these are:
- Express: A fast, lightweight, flexible framework for Node.js.
- bodyParser: A module that provides middle ware for extracting the entire body portion of an incoming request stream and exposes it on
req.body
as something easier to interface with. - cors: A Node.js package that provides middleware that can be used to enable cross-origin resource sharing with different options.
- Axios: A promise based HTTP client for JavaScript mainly used to send asynchronous HTTP requests to REST endpoints and perform CRUD operations.
Installing Pusher on the Client
We begin by including the Pusher Client library and Axios on our HTML page:
<body>
<script src="https://js.pusher.com/4.1/pusher.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.16.2/axios.js"></script>
</body>
In our script.js
file, we create a method called sendToServer()
where we perform a POST request with Axios. We pass in two parameters in our POST request; the first is the URI of our service endpoint and the second is our table data which we set as a value to a newly created constant which we call formData
sendToServer() {
const formData = this.buildForm();
axios.post('http://localhost:2000/record', formData)
.then(response => console.log(response));
}
We then establish our connection with Pusher by creating a new Pusher instance.
In our instance, we insert the free API key we get when signing up with Pusher. To ensure connection traffic is encrypted, we set encrypted
to the Boolean true
in our app.
var pusher = new Pusher('APP-KEY', {
cluster: 'CLUSTER',
encrypted: true
});
The key is part of the credentials that are generated when a new Channels app is created from your Pusher dashboard. You can create an account by signing up. See Appendix at the end of the article to learn how to setup a Pusher account.
Installing Pusher Channels on the Server
First, run the following command to install the server dependencies:
npm install express body-parser cors
A server.js
file is created; we then initialize Express after which we configure Express to support cross-origin resource sharing, encoded body and JSON. We then create a new Pusher instance which has an object containing our app ID, key, secret, cluster, and encryption preference.
Next, we create a POST route and in it we use Pusher to trigger an event named new-record
through a record
channel.
const express = require('express');
const bodyParser = require('body-parser')
const Pusher = require('pusher')
const cors = require('cors')
const app = express();
app.use(cors())
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
const pusher = new Pusher({
appId: 'APP-ID',
key: 'KEY',
secret: 'SECRET',
cluster: 'CLUSTER',
encrypted: true
});
app.post('/record', (req, res) => {
console.log(req.body);
pusher.trigger('records', 'new-record', req.body);
res.send('Pushed');
})
app.listen(2000, () => console.log('Listening at 2000'));
To get realtime updates on our table, in our script.js
file (client side) we use Pusher’s subscribe()
method to subscribe to our channel records
. We then define a callback function that binds our event new-record
and with it, our data.
var pusher = new Pusher('APP-KEY', {
cluster: 'CLUSTER',
encrypted: true
});
var channel = pusher.subscribe('records');
channel.bind('new-record', (data) => {
this.addRow(dataTable, data);
});
Conclusion
With this walk through, you should be able to build cross platform realtime apps with ease. Other DataTables and Pusher features are available but their use depends on the requirements of your app. You can have a look at the DataTables documentation and while you’re at it, take a glance at Pusher’s as well. For a deeper comprehension of the project, feel free to explore further on GitHub.
Appendix: Pusher Channels Setup
- Sign up for a free Pusher account:
- Create a new Channels app by selecting Apps on the sidebar and clicking Create New button on the bottom of the sidebar:
- Configure an app by providing basic information requested in the form presented. You can also choose the environment you intend to integrate Pusher with for a better setup experience:
- You can retrieve your keys from the App Keys tab:
20 April 2018
by Christian Nwamba