Chart.js not resorting rows based on plugin after using update function

空扰寡人 提交于 2019-12-24 07:57:47

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!