Skip to content

Instantly share code, notes, and snippets.

@bseib
Created December 13, 2017 13:48
Show Gist options
  • Save bseib/bb4ea32731376318a545d527752f88a1 to your computer and use it in GitHub Desktop.
Save bseib/bb4ea32731376318a545d527752f88a1 to your computer and use it in GitHub Desktop.
Vue + DataTables + load once
<!DOCTYPE html>
<html>
<head>
<title>Example Vue + DataTables</title>
<!-- I happened to be using the bootstrap styling w/ DataTables. You may not need this. -->
<link rel="stylesheet" href="/path/to/datatables/DataTables-1.10.16/css/dataTables.bootstrap4.min.css">
</head>
<body>
<div id="example-page">
<div v-show="isFirstDataLoaded" style="display:none">
<table id="personsTable" class="table table-striped table-bordered table-responsive table-sm" cellspacing="0" width="100%">
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>address</th>
<th>zip</th>
</tr>
</thead>
<tfoot>
<tr>
<th>id</th>
<th>name</th>
<th>address</th>
<th>zip</th>
</tr>
</tfoot>
<tbody>
<tr v-for="p in persons" :id="'row-'+p.personId">
<td v-text="p.personId"></td>
<td v-text="p.name"></td>
<td v-text="p.address"></td>
<td v-text="p.zip"></td>
</tr>
</tbody>
</table>
</div>
</div><!-- example-page -->
<script type="text/javascript" src="/path/to/datatables/DataTables-1.10.16/js/jquery.dataTables.min.js"></script>
<script type="text/javascript" src="/path/to/datatables/DataTables-1.10.16/js/dataTables.bootstrap4.min.js"></script>
<script type="text/javascript" src="/path/to/vue-datatables-example.js"></script>
</body>
</html>
(function() {
"use strict";
window.addEventListener("load", function() {
new Vue({
el : '#example-page',
data : {
isFirstDataLoaded: false,
},
created: function() {
// non-observable vars can go here
this.dataTable = null;
this.persons = [];
this.init();
},
methods: function() {
init: function() {
var self = this;
// using axios to get data from server
axios.get('/rest/call/to/persons').then(function(response) {
self.isFirstDataLoaded = true;
if ( isGood(response) ) {
self.persons = extractListOfData(response);
Vue.nextTick(function() {
// save a reference to the DataTable
self.dataTable = $('#personsTable').DataTable({
"paging": true,
"pageLength": 50,
"info": false,
// etc
});
});
}
else {
showWarning(response);
}
}).catch(function(error) {
showWarning(error);
});
},
},
// call this with the person once you've confirmed you really want to delete it.
// this deletes it from the DataTable, our non-observed list, and deletes it on the server too.
handleConfirmedDelete: function(person) {
var index = this.findIndex(person.personId);
if ( null != index ) {
// Found it in our non-observable list
var self = this;
// tell the server to delete the person
axios.post('/rest/call/to/person/delete/'+candidate.executionId).then(function(response) {
if ( isGood(response) ) {
// tell DataTable to remove a row by id (which we set in the html).
// the 'full-hold' is so that the pagination stays on the same page after deletion.
self.dataTable.row('#row-'+person.personId).remove().draw('full-hold');
// now clean up our list
self.persons.splice(index, 1);
}
else {
showWarning(response);
}
}).catch(function(error) {
showWarning(error);
});
}
else {
// Wasn't in our list, but I found some corner cases where DataTables still hangs on and will
// render an item that is no longer in our persons[] list. I could never pin it down, so I just
// did this simple workaround. I'd like to know if someone can pin it down.
self.dataTable.row('#row-'+person.personId).remove().draw('full-hold');
}
},
}); // Vue
});
}());
@bseib
Copy link
Author

bseib commented Dec 13, 2017

This is how I ended up combining Vue and DataTables. Both want to control the DOM, which will not work.

There some good content here: vuejs/vue#423

For my use case all I wanted from Vue was the power to easily render a <table> and then hand it over to DataTables and get out of the way. The strategy was to use a v-for that iterated on variable that didn't have all the "observable" stuff hooked to it.

This tip from Evan You showed how to use the created hook to create variables that will not have the observable machinery hooked to it, but is still accessible as a variable within the Vue component. Which means you can iterate on it with a v-for.

So what you end up with is a table rendered once by Vue, and then you pass that table on to the DataTables lib, and they will never interact with one another.

As for modifying the data table, I used the DataTables way of changing the data. My example did a delete only. I'm sure an add could be performed in a similar manner.

Also, my example was reading data from a server. Adapt as you wish.

Caveat emptor -- I copied and pasted working code, then edited out a bunch of stuff to make it generic, removed irrelevant stuff, made up a few functions just to convey the idea. But the code in the gist has not been tested. So you might find a typo or two. It's just to convey the idea.

@chieunhatnang
Copy link

You saved my day! Thank you.

@januaranas
Copy link

Saved me! =)) Thx

@camilobv
Copy link

camilobv commented Sep 8, 2018

thanks men

@moulayecisse
Copy link

You saved my life :) thx, i passed the whole day trying to make this work

@SamyOteroGlez
Copy link

Not working for me :((
it keeps complaining about datatable function is not there:

TypeError: $(...).DataTable is not a function

here is my component code.

Thanks in advance.

BTW I'm using same versions as you:

<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.16/css/dataTables.bootstrap4.min.css"/>
<script type="text/javascript" src="https://cdn.datatables.net/1.10.16/js/jquery.dataTables.min.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/1.10.16/js/dataTables.bootstrap4.min.js"></script>
<template>

    <div>
        <div class="clearfix"></div>
        <hr/>

        <div class="form-inline">

            <div class="form-group mb-2">
                <label for="gene">Gene name:</label>
                <input
                    id="gene"
                    name="gene"
                    type="text"
                    class="form-control form-control-sm mx-sm-3"
                    placeholder="Gene name..."
                    v-model="form.gene"
                />
            </div>

            <div class="form-group mb-2">
                <label for="experiment">Experiment id:</label>
                <input
                    id="experiment"
                    name="experiment"
                    type="text"
                    class="form-control form-control-sm mx-sm-3"
                    placeholder="Experiment id..."
                    v-model="form.experiment"
                    v-bind:disabled="!form.gene"
                />
            </div>

            <button @click="search" class="btn btn-primary btn-sm mb-2">Search</button>

        </div>

        <hr/>

        <div v-if="result" v-for="(rows, table) in result">
            <h4 class="mb-4">
                <button type="button" class="btn btn-outline-info btn-sm"
                    title="Collapse table."
                    data-toggle="collapse" v-bind:href="'#' +  table + '-retrieval'"
                    aria-expanded="true" v-bind:aria-controls="table + '-retrieval'">
                        +/-
                </button>
                {{ table | ucfirst }}
            </h4>

            <div v-bind:id="table + '-retrieval'" class="show">
                <table class="data-retrieval table table-hover table-bordered table-sm stripe row-border order-column"
                    style="width:100%">
                    <thead class="thead-light">
                        <tr>
                            <th style="width:60px;">Exp. Id</th>
                            <th style="width:200px;">Name</th>
                            <th style="width:500px;">Description</th>
                            <th>{{ rows['column'] | ucfirst }}</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr v-for="row in rows['data']">
                            <td>
                                <a v-bind:href="'/data/experiments/show/' + row.experiment_id">
                                    {{ row.experiment_id }}
                                </a>
                            </td>
                            <td>
                                {{ row.name }}
                            </td>
                            <td>
                                {{ row.description }}
                            </td>
                            <td>
                               <span v-for="(value, key) in JSON.parse(row[rows['column']])">
                                   <b>{{ key }}</b>: <span v-html="highlight(value, form.gene)"></span><br/>
                                </span>
                            </td>
                        </tr>
                    </tbody>
                </table>
            </div>

            <hr/>
        </div>

        <div v-if="!result" class="alert alert-primary" role="alert">
            No record found!
        </div>

    </div>

</template>

<script>
    import axios from 'axios';

    export default {
        data() {
            return {
                form: {
                    gene: null,
                    experiment: null
                },
                result: null,
                errors: []
            }
        },

        created() {
            this.datatable = null;
        },

        methods: {

            initDatatable(result) {
                var self = this;

                setTimeout(function() {
                    Vue.nextTick(function() {
                        for (var table in result) {
                            console.log(table);
                            var id = '#' + table + '-retrieval';
                            console.log($(id));
                            this.datatable = $(id).DataTable({
                                ordering: false
                            });
                        }
                    });
                }, 1000);

                // this.datatable = $('.data-retrieval').DataTable({
                //     ordering: false
                // });
            },

            /**
             * Search api call.
             */
            search() {
                axios.get('/api/data/retrieval/search', {
                    params: {
                        gene: this.form.gene,
                        experiment: this.form.experiment
                    }
                }).then(response => {
                    this.result = response.data.result;
                    this.form.gene = response.data.gene;
                    this.form.experiment = response.data.experiment;

                    this.initDatatable(this.result);
                }).catch(errors => {
                    this.errors.push(errors);

                    console.log(this.errors);
                });
            },

            /**
             * Highlight match.
             */
            highlight(value, pattern) {
                var regex = new RegExp('(\w*' + pattern + '\w*)', 'g');

                return value.replace(regex, replace => {
                    return '<span style="background:#7ae8a3">' + replace + '</span>';
                });
            }
        },

        filters: {

            /**
             * Set first letter to uppercase.
             */
            ucfirst: function (value) {
                if (value) {
                    return value.toString().charAt(0).toUpperCase() + value.substring(1)
                }

                return null;
            }
        }
    }
</script>

@bseib
Copy link
Author

bseib commented Aug 22, 2019

    console.log($(id));
    debugger; // <-- maybe stop here and have a look at things from the console to see why .DataTable() is not there...
    this.datatable = $(id).DataTable({
      ordering: false
    });

Does $(id) return the element okay and print it where you did your console log?

If yes, then jQuery is working, and it would seem that the DataTable lib is not loading correctly with jQuery. ??

@SamyOteroGlez
Copy link

Thanks!

Does $(id) return the element okay and print it where you did your console log?
Yes, it behave as expected.
I even checked if it brings the datatable from the remote server and it looks fine, I also checked (n times) the script load order.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment