Datatable not initialising. Vue.js and Gridstack (Laravel)
Datatable not initialising. Vue.js and Gridstack (Laravel)
Hello,
I'm really struggling to get my Datatable to initialise when my page loads.
This is a bit of a work in progress, so apologies if it is a bit of a mess.
If I do this in the console after the page load, the Datatable loads correctly.
$('#content-id-2').DataTable();
It looks like a timing problem but I'm not certain.
http://debug.datatables.net/exijuw
Here is the HTML template:
@extends('layouts.app')
@section('head')
<style>
.chart-sized {
height: 200px;
width: 50%;
background-color: powderblue;
}
</style>
<script src="{{ elixir('js/all.js') }}"></script>
@endsection
@section('content')
<div id="page-wrapper">
<div class="row">
<div class="col-lg-12">
<h1>Dashboard</h1>
<div class="alert alert-dismissable alert-warning">
<button data-dismiss="alert" class="close" type="button">×</button>
This is a message
</div>
</div>
</div>
<div class="row">
</div>
</div>
<h1>Gridstack.js and Vue.js</h1>
{{--https://troolee.github.io/gridstack.js/--}}
<div id="app">
<div class="grid-stack">
<chart-widget v-for="widget in chartsList" v-bind:widget="widget"></chart-widget>
<table-widget v-for="widget in tablesList" v-bind:widget="widget"></table-widget>
<gauge-widget v-for="widget in gaugesList" v-bind:widget="widget"></gauge-widget>
</div>
<br>
<button class="btn btn-default" id="save-grid" v-on:click="savePositions">Save Grid</button>
</div> {{--app--}}
<script src="/js/dashboard.js"></script>
@endsection
@section('js')
<script>
</script>
@endsection
and the JS.
//register a global component for gauges
Vue.component('gauge-widget', {
props: ['widget'],
template:
'<div class="grid-stack-item":data-gs-x="widget.x" :data-gs-y="widget.y" :data-gs-width="widget.width" :data-gs-height="widget.height" :id-for-save="widget.id">'+
'<div class="grid-stack-item-content" :id="widget.contentId"></div>'+
'</div>'
});
//register a global component for charts
Vue.component('chart-widget', {
props: ['widget'],
template:
'<div class="grid-stack-item":data-gs-x="widget.x" :data-gs-y="widget.y" :data-gs-width="widget.width" :data-gs-height="widget.height" :id-for-save="widget.id">'+
'<div class="grid-stack-item-content" :id="widget.contentId"></div>'+
'</div>'
});
//register a global component for tables
Vue.component('table-widget', {
props: ['widget'],
template: '<div class="grid-stack-item" :data-gs-width="widget.width" :data-gs-height="widget.height" :id-for-save="widget.id" :data-gs-x="widget.x" :data-gs-y="widget.y">'+
'<div class="panel panel-primary grid-stack-item-content " >' +
'<div class="panel-heading">' +
'<h3 class="panel-title"><i class="fa fa-bar-chart-o"></i>{{ widget.title }}</h3>' +
'</div>' +
'<div class="panel-body" >' +
'<table :id="widget.contentId" class="display" cellspacing="0" width="100%">'+
'<thead>'+
'<tr>'+
/* http://www.ctlevi.com/software/2015/02/26/vuejs-simple-paginated-table-with-search.html*/
'<th v-for="header in widget.headers">'+
'{{header}}'+
'</th>'+
'</tr>'+
'</thead>'+
'<tbody>'+
'<tr v-for="row in widget.data">' +
'<td v-for="column in row">'+
' {{ column }}'+
'</td>'+
'</tr>' +
'</tbody>'+
'</table>'+
'</div>' +
'</div>' +
'</div>'
});
window.onload = function () {
vm = new Vue({
el: '#app',
data: function () {
return {
chartsList: [],
gaugesList: [],
tablesList: []
};
},
mounted: function () {
axios.get('/dashboardWidgets?dashboard=1')
.then(function (response) {
vm.chartsList = response.data.chartsList;
vm.gaugesList = response.data.gaugesList;
vm.tablesList = response.data.tablesList;
initialiseWidgets();
})
.catch(function (error) {
console.log(error);
});
},
methods: {
savePositions: function (event) {
//This does not seem very Vue like
//I really want to bind these somehow so I don't have to get them from the DOM
var widgetPositions = _.map($('.grid-stack .grid-stack-item:visible'), function (el) {
el = $(el);
var node = el.data('_gridstack_node');
return {
id: el.attr('id-for-save'),
x: node.x,
y: node.y,
width: node.width,
height: node.height
};
});
axios.post('/savePositions', {
params: {
positions: widgetPositions
}
})
.then(function (response) {
console.log(response);
alert('Done');
})
.catch(function (error) {
console.log(error);
});
}
}
});
}//end of onload
function initialiseWidgets() {
console.log('loading charts')
vm.chartsList.forEach(initialiseChart);
console.log('loading gauges')
vm.gaugesList.forEach(initialiseGauge);
//now we have the widgets we can fire up Gridstack
console.log('loading gridstack')
/*Gridstack*/
$(function () {
var options = {
cellHeight: 100,
verticalMargin: 10
/*disableResize: true*/
};
$('.grid-stack').gridstack(options);
});
`` console.log('loading tables')
vm.tablesList.forEach(initialiseTable);
}//initialiseWidgets
function initialiseGauge(item,index){
FusionCharts.ready(function () {
var fusionGauge = new FusionCharts({
"type": item['type'],
"renderAt": item['contentId'],
"width": "100%",
"height": "100%",
"dataFormat": "json",
"dataSource": {
"chart": {
"caption": "Customer Satisfaction Score",
"subcaption": "Last week",
"lowerLimit": "0",
"upperLimit": "100",
"theme": "fint"
},
"colorRange": {
"color": [
{
"minValue": "0",
"maxValue": "50",
"code": "#e44a00"
},
{
"minValue": "50",
"maxValue": "75",
"code": "#f8bd19"
},
{
"minValue": "75",
"maxValue": "100",
"code": "#6baa01"
}
]
},
"dials": {
"dial": [
{
"value": "67"
}
]
}
}
});
fusionGauge.render();
})
}
function initialiseTable(item,index){
test = $('#content-id-2').DataTable();
console.log(test);
}
function initialiseChart(item,index){
FusionCharts.ready(function () {
var fusionChart = new FusionCharts({
"type": item['type'],
"renderAt": item['contentId'],
"width": "100%",
"height": "100%",
"dataFormat": "json",
"dataSource": {
"chart": {
"caption": item['title'],
"subCaption": item['description'],
"xAxisName": "Month",
"yAxisName": "Revenues (In USD)",
"theme": "fint"
},
"data": item['data']
}
});
fusionChart.render();
});
}//initialiseChart
$("#menu-toggle").click(function(e) {
e.preventDefault();
$("#wrapper").toggleClass("toggled");
});
This discussion has been closed.
Replies
Just hacked this in:
and the Datatable now initialises correctly.
This confirms it is a timing problem, just not sure how to handle it in a sensible way.
Mick
It does indeed sound a lot like a timing issue.
I'm not familiar with Vue.js at all I'm afraid. Does it have a callback to say when the widget has been initialised? If so, that would be the time to initialise DataTables.
Allan
Yes,
mounted:
https://vuejs.org/v2/guide/instance.html#Lifecycle-Diagram
This is a tricky one, there is a lot of stuff going on.
In mounted, I call initialiseWidgets, which calls vm.tablesList.forEach(initialiseTable); which is where I want to fire up the Datatable (anywhere would be better than using the timer).
I did have this working, when all the code was in the template, now I'm trying to refactor it all out.
Mick.
HI Mick,
I hate to say it, but I think this might be one for the Vue.js folks. If the element isn't in the DOM when the mount happens, it sounds like there is something else going on.
Allan
No worries. Allan.
Thanks for looking. I will let you know how I get on.
i still have some refactoring to do, so that might change the timings a bit.
Mick
FYI I stuck this in:
console.log($('#content-id-2'));
and the table doesn't exist at this point, so no way to convert it into a DT.
There is also an updated callback. I have stuck the initialise in there and it's now working.
Brilliant! Good to hear you'e got it working.
Allan