2

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?

Cameron Tinker
  • 9,119
  • 10
  • 42
  • 80
  • 1
    You need to move the `$(window).resize(respondCanvas);` outside of your ajax success callback. With the current approach you are subscibing on the resize event multiple times which probably results in the multiple chart rendering... – nemesv May 02 '14 at 19:50
  • 1
    `ctx.width = ctx.width` doesn't appear 100% reliable for clearing the canvas (see the comments on [this answer](http://stackoverflow.com/a/2142549/3490792)). Try using `ctx.clearRect(0, 0, ctx.width, ctx.height)` instead. – Charlie May 02 '14 at 20:19
  • 1
    Correction, last comment should say: Try using `ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)` instead. – Charlie May 02 '14 at 20:59
  • None of the answers worked for me so I deleted the element and dynamically recreated them using `document.createElement('canvas')` and remade the context and graph instance then redrew chart on the new canvas and fixes my issue - clearing logic never did work (chart.js api is at version 1 so that could be the issue in my case) – Coty Embry Sep 10 '18 at 13:59

1 Answers1

0

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();
});
Cameron Tinker
  • 9,119
  • 10
  • 42
  • 80