问题
I'm displaying a stacked bar chart on my page. When I initialize the table, I sort the data so that it is displayed in descending order by total in each dataset.
To do this, I'm using a custom plugin
Chart.plugins.register({
datasets: [],
getData(labels, datasets) {
const sum = [];
for (i = 0; i < datasets[0].length; i++) {
sum.push({
label: labels[i],
data: datasets.map(e => e[i]),
get sum() { // ES6 - getter
return this.data.reduce((a, b) => a + b);
}
});
}
return sum;
},
beforeInit(chart) {
chart.data.datasets.forEach((dataset, datasetIndex) => {
this.datasets.push(dataset.data);
});
const data_store = this.getData(chart.data.labels, this.datasets).sort((a,b) => b.sum - a.sum);
data_store.forEach((d,i) => {
chart.data.labels[i] = d.label;
d.data.forEach((v, vi) => {
chart.data.datasets[vi].data[i] = v;
});
});
}
});
Now, every 60 seconds I am running some AJAX to grab updated data to refresh the chart.
setInterval(function() {
$.get("ajax?type=chart_update&id={{ @GET.id }}", function(data){
var data = JSON.parse(data);
data.datasets.forEach(function(value, key) {
chart_data.datasets[key].data = value.data;
});
chart_data.labels = data.labels;
myChart.update();
});
}, 60000);
However, when I run the update function, the data is no longer sorted. I've tried to use different hooks in my plugin instead of beforeInit
such as beforeUpdate
, beforeRender
, and beforeDraw
, but these either made no difference, makes the chart render weirdly, or I get the error
Uncaught TypeError: Cannot read property 'data' of undefined
How can I sort the data again after updating?
回答1:
The sorting is quite complexe because you can't simply change the data, you need to know the order for the labels as well. You find my code in the JSBin. I hope this is what you want.
Complete code:
let chartOptions = {
scales: {
xAxes: [{
stacked: true
}],
yAxes: [{
stacked: true
}]
}
}
let chartData = {
labels: ["FirstValue", "SecondValue", "ThirdValue", "FourthValue"],
datasets: [{
label: 'FirstDataset',
data: [12, 55, 33, 85],
backgroundColor: 'red'
},{
label: 'SecondDataset',
data: [95, 15, 73, 5],
backgroundColor: 'blue'
},{
label: 'ThirdDataset',
data: [38, 17, 73, 45],
backgroundColor: 'green'
},{
label: 'FourthDataset',
data: [49, 89, 33, 85],
backgroundColor: 'orange'
}]
}
let sortedData = sortData(chartData)
let ctx = document.getElementById("myChart");
let stackedBar = new Chart(ctx, {
type: 'bar',
data: sortedData,
options: chartOptions
});
function sortData(chartData) {
console.log('#############')
console.log('beforeSort', chartData)
let arraySum = []
let sortedData = {}
sortedData.labels = []
sortedData.datasets = []
// Calculate the total sum of all datasets for each label
for (let i = 0; i < chartData.labels.length; i++) {
let dataSum = 0
chartData.datasets.map(obj => {
dataSum += obj.data[i]
})
arraySum.push(dataSum)
}
//console.log("Data sum", arraySum)
// Get a lookup-table with the order of the labels
let arrayLookupTable = []
for (let i = 0; i < chartData.labels.length; i++) {
let index = arraySum.indexOf(Math.max.apply(null,arraySum))
arrayLookupTable.push(index)
arraySum[index] = -1
}
//console.log("arrayLookupTable", arrayLookupTable)
// Assign the sorted labels
for (let i = 0; i < chartData.labels.length; i++) {
sortedData.labels.push(chartData.labels[arrayLookupTable[i]])
}
// Assign the sorted datasets
for (let i = 0; i < chartData.datasets.length; i++) {
sortedData.datasets.push(chartData.datasets[i])
//previous line copies complete dataset with old data which gets sorted in the next line
sortedData.datasets[i].data = changeDataOrder(sortedData.datasets[i].data, arrayLookupTable)
}
console.log("afterSort", sortedData)
return sortedData
}
function changeDataOrder(previousDataArray, arrayLookupTable) {
//console.log('changeDataOrder', previousDataArray, arrayLookupTable)
let newDataArray = []
for (let i = 0; i < arrayLookupTable.length; i++) {
newDataArray.push(previousDataArray[arrayLookupTable[i]])
}
return newDataArray
}
function updateChart() {
chartData.datasets.forEach(function(element) {
element.data = r(chartData.labels.length);
});
let updatedChart = sortData(chartData)
stackedBar.config.data.labels = updatedChart.labels; //labels have to be updated separately
stackedBar.update();
}
function r(length) {
let values = [];
for (i = 0; i < length; i++) {
values.push(Math.floor(Math.random() * 100));
}
return values;
}
来源:https://stackoverflow.com/questions/58105362/chart-js-not-resorting-rows-based-on-plugin-after-using-update-function