Search code examples
javascriptcanvasjs

CanvasJS not properly rendering chart


I am using the following code to render an OHLC chart in CanvasJS:

<script>

var candleData = [];
var chart = new CanvasJS.Chart("chartContainer", {
    title: {
        text: 'Demo Stacker Candlestick Chart (Realtime)'
    },
    zoomEnabled: true,
    axisY: {
        includeZero: false,
        title: 'Price',
        prefix: '$'
    },
    axisX: {
        interval: 1,
    },
    data: [{
            type: 'ohlc',
            dataPoints: candleData
        }
    ]
});

function mapDataToPointObject(data) {
    var dataPoints = [];
    for(var i = 0; i < data.length; i++) {
        var obj = data[i];
        var newObj = {
            x: new Date(obj.time),
            y: [
                obj.open,
                obj.high,
                obj.low,
                obj.close
            ]
        }
        dataPoints.push(newObj);
    }
    return dataPoints;
}

function updateChart() {
    $.ajax({
        url: 'http://localhost:8080',
        success: function(data) {
            candleData = JSON.parse(data);
            candleData = mapDataToPointObject(candleData);
            chart.render();
        }
    });
}

$(function(){
    setInterval(() => {
        updateChart();
    }, 500);
});

The data properly loads, parses into the correct format, and render() is called on the interval like it should. The problem is, while the chart axes and titles all render properly, no data shows up. The chart is empty.

What DOES work is setting the data directly to the chart using

chart.options.data[0].dataPoints = candleData;

Why does my above solution not work then? Is there a way I can update the chart's dataPoints without having to hardcode a direct accessor to the chart's dataPoints?


Solution

  • It's related to JavaScript pass by value and pass by reference.

    After execution of the following line.

    dataPoints: candleData

    dataPoints will refer to the current value of candleData. ie. dataPoints = [];

    Now if you redefine candleData to any other value.

            candleData = JSON.parse(data);
            candleData = mapDataToPointObject(candleData);
    

    Then dataPoints won't be aware of this update and will still refer to the empty array (that you pointed earlier).

    The following snippet will make it easy to understand

    //pass by value
    var a = "string1";
    var b = a;
    a = "string2";
    
    alert("b is: " + b); //this will alert "string1"
    
    
    //pass by reference
    var c = { s: "string1" };
    var d = c;
    c.s = "string2";
    
    alert("d.s is: " + d.s); //this will alert "string2"

    For more, you can read about pass by value and pass by reference.

    Javascript by reference vs. by value

    Explaining Value vs. Reference in Javascript