Using DataTables with Vue

The problem (and solution)

I work with two fantastic JavaScript libraries for my two largest work projects:

I ran into a perplexing problem the other day that took me a few hours to solve. If you are reading this, I imagine you ran into the same problem, and I hope this will help you solve it in quicker time.

Problem

I experienced the following symptoms:

  • Clicking on column headers failed to change the sorting
  • The search box failed to filter the data down
  • DataTable failed to update after calling myDataTable.clear().rows.add(newData).draw()

Cause

This problem happens when:

  • Your DataTable resides as a descendant of the HTML element you bind Vue to
  • You configure/initialize the DataTable object before you configure/initialize the Vue object

I don't know what happens exactly, but it seems due to Vue's reactive nature that it hijacks the root HTML element's and descendants' events which it is assigned to, and that renders the DataTable inert.

Solution

There are two ways to solve this problem:

Ideal

Configure your DataTable during the "mounted" event of your Vue object.

Less Ideal

Move your DataTable HTML element outside of the root HTML element Vue is bound to.

Example

Below you will see two DataTables that are powered by the same data. The broken one is configured/initialized before the Vue object, and the working one is configured/initialized after the Vue object.

You will see the symptoms I experienced in the broken DataTable and see how it is supposed to work with the Working example.

As you change the Game Category option you will see the Working DataTable update, but not the Broken DataTable.

Broken Datatable

Working Datatable

More explanation

DataTables is dependent on jQuery, so I will use jQuery's "ready" function for the page's initialization code.

The Broken DataTable Code

JavaScript

<script> $(document).ready(function(){ // Creating/Initializing the DataTable before the Vue object leaves it in a broken state after Vue is created/initialized. const brokenDataTable = $('#broken-datatable').DataTable({ data: trackmaniaData, columns: [ { data: 'title', title: 'Title' }, { data: 'platform', title: 'Platform' } ] }); const vm = new Vue({ el: '#page', data: { gameCategory: 'trackmania', gameCategories: ['trackmania', 'other'], gamesData: trackmaniaData, brokenDataTable: brokenDataTable, }, methods: { updateGamesData() { console.log('updateGamesData with category: ', this.gameCategory); switch (this.gameCategory) { case 'trackmania': this.gamesData = trackmaniaData; break; case 'other': this.gamesData = otherGamesData; break; default: console.log('option not recognized'); break; } } }, watch: { gameCategory(newGameCategory) { this.updateGamesData(); }, gamesData(newGamesData) { this.brokenDataTable.clear().rows.add(newGamesData).draw(); } } }); }); </script>