2
votes

I'm trying to draw two charts using the Google Charts API. I set up my HTML like this:

<div id="page_views" data-title="{{ report['page_views']['title'] }}" data-labels="{{ report['page_views']['labels'] }}" data-rows="{{ report['page_views']['rows'] }}"></div>
<div id="event_views" data-title="{{ report['event_views']['title'] }}" data-labels="{{ report['event_views']['labels'] }}" data-rows="{{ report['event_views']['rows'] }}"></div>

where the data attributes are filled during template rendering. I then use the following javascript to attempt to draw my charts:

google.load('visualization', '1.0', {packages: ['line']});
google.setOnLoadCallback(drawCharts);

function drawPageViews() {
    var data = new google.visualization.DataTable();
    var page_views = document.getElementById("page_views");
    var labels = eval(page_views.dataset.labels);
    data.addColumn('number', "Day");
    for(var i = 0; i < labels.length; i++) {
        data.addColumn('number', labels[i]);
    }

    var rows = eval(page_views.dataset.rows);
    data.addRows(rows);

    var options = {
        chart: {
            title: page_views.dataset.title
        },
        width: 900,
        height: 500
    };

    var chart = new google.charts.Line(document.getElementById('page_views'));
    chart.draw(data, options);
}

function drawEventViews() {
    var data = new google.visualization.DataTable();
    var event_views = document.getElementById("event_views");
    var labels = eval(event_views.dataset.labels);
    data.addColumn('number', "Day");
    for(var i = 0; i < labels.length; i++) {
        data.addColumn('number', labels[i]);
    }

    var rows = eval(event_views.dataset.rows);
    data.addRows(rows);

    var options = {
        chart: {
            title: event_views.dataset.title
        },
        width: 900,
        height: 500
    };

    var chart = new google.charts.Line(document.getElementById('event_views'));
    chart.draw(data, options);
}

function drawCharts() {
    drawPageViews();
    drawEventViews();
}

The result that I get is that one of the charts is drawn while the other contains an SVG with an empty tag and nothing else inside. Which chart gets drawn is random. Commenting out either draw function makes the other single chart draw as expected.

It seems like there must be some sort of shared global state or variable but it looks to me like everything is defined in the different draw functions. When I look up similar questions people offer solutions which look very much like what I'm doing. What am I missing?

1
there's a bug with multiple material charts, see my answer hereWhiteHat

1 Answers

2
votes

It seems this behavior is related with draw function, in particular it occurs once multiple charts is rendered on the page.

According to the documentation:

The draw() method is asynchronous: that is, it returns immediately, but the instance that it returns might not be immediately available.

For rendering multiple charts on the page you could consider the following approach: render the next chart once the previous one is rendered, this is where ready event comes to the rescue.

Having said that the solution would be to replace:

function drawCharts() {
  drawPageViews();
  drawEventViews();
}

with

function drawCharts() {
    drawPageViews(function(){
       drawEventViews();
    });
}

where

function drawPageViews(chartReady) {
    //...
    var chart = new google.charts.Line(document.getElementById('page_views'));
    if (typeof chartReady !== 'undefined') google.visualization.events.addOneTimeListener(chart, 'ready', chartReady);
    chart.draw(data, options);
}

and

function drawEventViews(chartReady) {
    //...
    var chart = new google.charts.Line(document.getElementById('event_views'));
    if (typeof chartReady !== 'undefined') google.visualization.events.addOneTimeListener(chart, 'ready', chartReady);
    chart.draw(data, options);
}

Working example

google.load('visualization', '1.0', { packages: ['line'] });
google.setOnLoadCallback(drawCharts);

function drawPageViews(chartReady) {
    var data = new google.visualization.DataTable();
    var page_views = document.getElementById("page_views");
    var labels = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];

    data.addColumn('string', 'Day');
    data.addColumn('number', 'PageViews');

   
    var rows = new Array();
    for (var i = 0; i < labels.length; i++) {
        rows.push([labels[i], getRandomInt(0, 100)]);
    }
    data.addRows(rows);


    var options = {
        chart: {
            title: 'Page views'
        },
        width: 900,
        height: 500
    };

    var chart = new google.charts.Line(document.getElementById('page_views'));
    if(typeof chartReady !== 'undefined') google.visualization.events.addOneTimeListener(chart, 'ready', chartReady);
    chart.draw(data, options);
}

function drawEventViews(chartReady) {
    var data = new google.visualization.DataTable();
    var event_views = document.getElementById("event_views");
   
    var labels = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];

    data.addColumn('string', 'Day');
    data.addColumn('number', 'EventViews');
    var rows = new Array();
    for (var i = 0; i < labels.length; i++) {
        rows.push([labels[i], getRandomInt(0, 100)]);
    }
    data.addRows(rows);

    var options = {
        chart: {
            title: 'Event views'
        },
        width: 900,
        height: 500
    };

    var chart = new google.charts.Line(document.getElementById('event_views'));
    if(typeof chartReady !== 'undefined') google.visualization.events.addOneTimeListener(chart, 'ready', chartReady);
    chart.draw(data, options);
}

function drawCharts() {
    drawPageViews(function(){
        drawEventViews();
    });
}


function getRandomInt(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}
<script type="text/javascript" src="https://www.google.com/jsapi"></script> 
<div id="page_views"></div>
<div id="event_views"></div>