Photo Credit: Sam Stephenson

Turbolinks 5 and DataTables working together in harmony

Greg Blass
Published in
4 min readFeb 16, 2017

--

Turbolinks 5 is an awesome JavaScript library that makes your web application faster by eliminating full page reloads. It replaces only the body of the page using AJAX, so JavaScript and CSS are loaded only once. In addition, it uses a cached version if the page has already been viewed.

This allows smaller teams to get the performance benefits of a Single Page Application without the added cost or complexity. I use it on all my Rails projects and clients are always blown away by the user experience and speed of their app.

Turbolinks has been around for a while, but it had its caveats, and thus faced a good deal of resistance from the Rails community. The engineers at Basecamp (thank you Sam Stephenson) then re-wrote the library from the ground-up, fixed the wonkiness, and released Turbolinks 5 — and it has been glorious ever since.

DataTables Compatibility

One of the only issues I’ve run into is getting it to work with DataTables, my go-to JavaScript library for sortable, paginated, AJAX-based tables.

One main issue was happening:

On a restoration visit, the filtering controls would be added again every time you load the page since the DataTables init code would get called again.

My first idea was to add a class to the table on first init, and then only initialize DataTables that haven’t already been initialized. That way you could just load the page from the cache, essentially do nothing, and be on your merry way.

But this doesn’t work. I still to this day do not know why.

If you don’t re-initialize the DataTable after loading it from the cache in a restoration visit, none of the functionality will work (sorting, etc).

So the ‘good enough’ method I came up with to solve this was to destroy the DataTable before caching the page:

var dataTable = $('.data-table').DataTable();document.addEventListener("turbolinks:before-cache", function() {
if (dataTable !== null) {
dataTable.destroy();
dataTable = null;
}
});

This worked great! Success? For a while, yes. It allowed restoration visits to work without the double-init bug. I used this design pattern in production, and it worked well — But:

By nuking the table and re-initializing it, the user loses the current state of the table (search, filter, page).

I wasn’t quite sure what to do about this at first, but I didn’t have the time to invest, so I moved on with a less-than ideal user experience. More on this later.

Two AJAX Requests

Sometime in the future I noticed something happening in my console:

Restoration visits caused two requests to go out to the server (in the case of AJAX-based tables).

This made sense: once on the preview, once on the actual page visit.

Then I realized (read: found a suggestion on stack overflow): Why cache the page at all? You’re going to need to do an AJAX call anyway on the reinitialization of the table, so there’s no need to cache the page. Using this design pattern, I disabled caching on any index page that had AJAX-driven DataTables, and that fixed the double request issue!

Table State Saving

After being so close to finally having Turbolinks 5 and DataTables work how I wanted them to, I decided to dive into the state saving issue. As it turns out, DataTables has a stateSave option!

It uses localStorage, but you can override the callbacks to use sessionStorage instead, which would be cleared out when closing the browser tab. Or you could use a cookie, store stuff on the server — whatever you want.

The default localStorage was ideal for my use case, so I went with the default options. However, the state saving just didn’t work at first. I assumed the issue was due to Turbolinks, but after taking a look at the localStorage hash in the console, I could tell what was happening pretty quickly:

Every time a DataTable was initialized, it got assigned a new ID by DataTables, which the localStorage was using as the hash key — so the correct state was never getting loaded.

Why was DataTables using a new ID with an incremented number every time a table was initialized? After googling for more time than I would have liked, I finally realized the (very simple) solution:

To prevent DataTables from assigning every table initialized its own unique ID with an incrementing number at the end…just set the ID of the table explicitly yourself.

Now the stateSave option just worked!

Conclusion

And there you have it: Turbolinks 5 and DataTables working together in harmony. If you’re still starting new Rails applications and not using Turbolinks 5, I’d highly encourage you to take the time to learn the nuances of it. Your clients will thank you.

Hey there! I’m Greg, and I build web and mobile apps.

I’m a full-stack software engineer and consultant based in Philadelphia. I love using Ruby on Rails and React Native to build solutions for all types of businesses.

If you’re looking to build a beautiful product for your company, reach out to me at greg@phillydevshop.com, or find out more at www.phillydevshop.com.

--

--

Software Engineer and Web/App Developer. DreamIt Philly Alum. Lover of Ruby on Rails, React/React Native, Progressive Web Apps, Live Music, Craft Beer and Jessi