1

I am trying to fetch more data to my chartjs line chart (or use some backup data cached) onZoom/onPan, but having many issues doing this.

My initial code base is very complex which I can't copy here completely but I'll share what I tried already.

1st solution: Chartjs-plugin-zoom shares onZoom and onPan callbacks as mentioned here but these doesn't provide any context (chart, x, y) with these call backs, and calls while panning/zooming and it is not even correct.

This PR addresses few issues but it is not merged yet: Link

2nd solution: I am trying to get change data in chart js in beforeUpdate callback, but it is also throwing many issues to me. I am showing around 1000 datapoints in a graph by default and as my graph is updating every second so what I am trying to do is to have a backup of data which will contain passed datapoints outside of 1000 datapoints, so when someone zoomOut I am checking the coordinates of left and right and if these are changed (increase) I want to increase data in the dataset in beforeUpdate callback.

scales: {
    xAxes: [{
                    id: 'x-axis-0',
                    type: 'time',
                    beforeUpdate: function (chart) {
                        const scale = chart.chart.scales['x-axis-0']
                        if (scale.margins) {
                            const left = scale.getValueForPixel(scale.left)
                            const right = scale.getValueForPixel(scale.right)
                            const diffSec = (right - left) / 1000;
                            if (diffSec < 240) {
                                chart.chart.options.scales.xAxes[0].time.unit = 'second'
                            } else if (diffSec < 4*3600) {
                                chart.chart.options.scales.xAxes[0].time.unit = 'minute'
                            } else {
                                chart.chart.options.scales.xAxes[0].time.unit = 'minute'
                                chart.chart.options.scales.xAxes[0].time.stepSize = 10
                            }
                        }
// Ignore this if condition it is just for testing/debugging
                        if (chart.chart.data.labels.length > 1) {
                            // there are three line in graph
                            chart.chart.data.datasets[0].data = getMinArray(diffSec)
                            chart.chart.data.datasets[1].data = getAvgArray(diffSec)
                            chart.chart.data.datasets[2].data = getMaxArray(diffSec)
                            chart.chart.data.labels = getLabels(diffSec)
                            chart.update()
                          }
                    }
    }

In second method I am getting more errors like:

TypeError: Cannot read property 'hidden' of undefined

Do you've a 3rd solution? please throw it below.

EDIT: I've tried with different versions

"chart.js": "2.8.0",
"chartjs-plugin-zoom": "^0.7.0",

It seems that 1st solution is working now, but there is another problem:

  • onZoom is being called while zooming rather than

    Function called once zooming is completed

    as mentioned in Readme so this wont really help in fetching data from API.

2 Answers2

0

Chartjs-plugin-zoom shares onZoom and onPan callbacks as mentioned here but these doesn't provide any context (chart, x, y) with these call backs

There actually is context provided. It is called with an object containing the chart so you can do:

function callback(context) {
  console.log('Here is the chart: ' + context.chart);
}
Ben McCann
  • 18,548
  • 25
  • 83
  • 101
  • Ben McCann thanks for commenting, in the **EDIT** part of the question I've added that, yes with different versions of `chart.js` and `chartjs-plugin-zoom` it provides context, but the thing that call back is called while zooming not when zooming stops so it is not really helpful in fetching dynamic data. Please correct me if I am wrong or you can share any other solution. – Muhammad Arslan Jun 24 '19 at 04:34
  • There's an ongoing discussion about fixing that: https://github.com/chartjs/chartjs-plugin-zoom/pull/237 – Ben McCann Jun 26 '19 at 14:42
0

I found the solution and it is working well in beforeUpdate callBack.

I am having 1-3 datasets in my graphs and the solution works for any number of datasets you want to put But for a time series only with having allLabels and allData to utilise in graph.

I am having a more complex use case but sharing simplest code here for beforeUpdate and filterData code:


function filterData(chartInstance, leftLegendTime) {

    let diff = chartInstance.data.labels[0].getTime() - leftLegendTime
    // Check min 10 sec difference between graph and zoomed out graph to get safe from over updates
    if (!chartInstance.data.labels || diff === 0 || diff <= 10000) return

    let allLabels = chartInstance.data.datasets[0].allLabels

    // Matching graph's left axis start...
    let leftIndex = allLabels.map(
        (elem) => parseInt(elem.getTime()/1000)
    ).indexOf(parseInt(leftLegendTime/1000))

    if (leftIndex === -1) return;

    chartInstance.data = {...chartInstance.data, labels: allLabels.slice(leftIndex)}

    for (let i = 0;i < chartInstance.data.datasets.length; i++) {
        chartInstance.data.datasets[i] = {
            ...chartInstance.data.datasets[i],
            data: chartInstance.data.datasets[i].allData.slice(leftIndex)
        }
    }

    chartInstance.update()
}


// ...in graphOptions:
scales: {
        xAxes: [{
        id: 'x-axis-0',
        type: 'time',
        beforeUpdate: function (chart) {
        const scale = chart.chart.scales['x-axis-0']
        if (scale.margins) {
            const left = scale.getValueForPixel(scale.left)
            filterData(chart.chart, left._d.getTime())
        }
     }
}],
zoom: {
    enabled: true,
    drag: false, // cannot have drag-to-zoom and pan at once
    mode: 'x',
    rangeMax: {
        x: new Date(Date.now() + 1000)
    },
    rangeMin: {
        x: new Date(Date.now() - 900 * 1000) // eg. to show max history...
    }
}