Question

I've got some chart data that I load through an AJAX call and display with a modified Chart.js that supports tooltips. It works well for the first call, but when I do subsequent calls by changing date ranges, it draws the new chart data on top of the old chart data. I have put my chart refresh code into the window resize event so that it can be responsive. However, this may in fact be what is causing the problem. Here is my code:

var BreakDowns = [
    'Daily',
    'Weekly',
    'Monthly',
    'Quarterly',
    'Yearly'
];

var BreakDown = ko.observable(BreakDowns[0]);
BreakDown.subscribe(function (newValue) {
    switch (newValue) {
        case 'Daily':
            EndDate(StartDate());
            break;
        case 'Weekly':
            EndDate(moment(StartDate()).add('d', 7).format('MM/DD/YYYY'));
            break;
        case 'Monthly':
            EndDate(moment(StartDate()).add('M', 1).format('MM/DD/YYYY'));
            break;
        case 'Quarterly':
            EndDate(moment(StartDate()).add('M', 3).format('MM/DD/YYYY'));
            break;
        case 'Yearly':
            EndDate(moment(StartDate()).add('y', 1).format('MM/DD/YYYY'));
            break;
    }
    GetChartData();
});

var date = new Date();

var StartDate = ko.observable(moment(date).format('MM/DD/YYYY'));
var EndDate = ko.observable(moment(date).format('MM/DD/YYYY'));

var Form = ko.validatedObservable({
    StartDate: StartDate.extend({ isDate: true }),
    EndDate: EndDate.extend({ isDate: true })
});      

StartDate.subscribe(function (newValue) {
    GetChartData();
});

EndDate.subscribe(function (newValue) {
    GetChartData();
});


var chart = {};

var GetChartData = function () {
    if (Form.isValid()) {
        $.ajax({
            url: serviceUri + 'api/Document/ChartData/?breakdown=' + BreakDown().toLowerCase() + '&startDate=' + StartDate() + '&endDate=' + EndDate(),
            method: 'GET',
            dataType: 'json',
            success: function (d) {   
                var chartData = {
                    labels: d.AxisLabels,
                    datasets: [
                        {
                            fillColor: "rgba(220,220,220,0.5)",
                            strokeColor: "rgba(220,220,220,1)",
                            pointColor: "rgba(220,220,220,1)",
                            pointStrokeColor: "#fff",
                            data: d.DataSets[0]
                        }
                    ]
                };

                var max = Math.max.apply(Math, d.DataSets[0]);
                var steps = 10;

                var c = $('#summary');
                var ctx = c.get(0).getContext("2d");
                var container = c.parent();

                $(window).resize(respondCanvas);

                function respondCanvas() {
                    var $container = $(container);
                    if ($container.width() >= 900)
                        c.attr('width', $container.width());
                    else
                        c.attr('width', 900);

                    if ($container.height() >= 400)
                        c.attr('height', $container.height());                
                    else
                        c.attr('height', 400);

                    ctx.width = ctx.width;

                    //Call a function to redraw other content (texts, images etc)
                    chart = new Chart(ctx).Bar(chartData, {
                        scaleOverride: true,
                        scaleSteps: steps,
                        scaleStepWidth: Math.ceil(max / steps),
                        scaleStartValue: 0,
                        annotateDisplay: true
                    });
                }

                respondCanvas();
            }
        });
    }
}

Here is an image of what the resulting chart looks like: Mangled chart

How do I get Chart.js to not draw over the previous data and draw a new chart?

Was it helpful?

Solution

After moving $(window).resize(respondCanvas) out of my AJAX success callback, here is a simplified version of what I have that is working:

var chartData = {};
var max = 0;
var steps = 10;

function respondCanvas() {
    var c = $('#summary');
    var ctx = c.get(0).getContext("2d");
    var container = c.parent();

    var $container = $(container);

    c.attr('width', $container.width()); //max width

    c.attr('height', $container.height()); //max height                   

    //Call a function to redraw other content (texts, images etc)
    var chart = new Chart(ctx).Bar(chartData, {
        scaleOverride: true,
        scaleSteps: steps,
        scaleStepWidth: Math.ceil(max / steps),
        scaleStartValue: 0,
        annotateDisplay: true
    });
}

var GetChartData = function () {
    $.ajax({
        url: serviceUri,
        method: 'GET',
        dataType: 'json',
        success: function (d) {
           chartData = {
                labels: d.AxisLabels,
                datasets: [
                    {
                        fillColor: "rgba(220,220,220,0.5)",
                        strokeColor: "rgba(220,220,220,1)",
                        pointColor: "rgba(220,220,220,1)",
                        pointStrokeColor: "#fff",
                        data: d.DataSets[0]
                    }
                ]
            };

            max = Math.max.apply(Math, d.DataSets[0]);
            steps = 10;

            respondCanvas();
        }
    });
};

$(document).ready(function() {
    $(window).resize(respondCanvas);

    GetChartData();
});

Note, if you want to insert a small delay between updates, you can use a timeout:

$(document).ready(function() {
    $(window).resize(setTimeout(respondCanvas, 500));

    GetChartData();
});
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top