google / google-visualization-issues

288 stars 35 forks source link

gstatic loader.js doesn't support method 'arrayToDataTable' #2433

Open PaulYoum opened 7 years ago

PaulYoum commented 7 years ago

This bug is occuring on IE 11. The gstatic.com/charts/loader.js is failing periodically on load by throwing an exception at line 215, column 67 with an error of:

0x800a01b6 - JavaScript runtime error: Object doesn't support property or method 'arrayToDataTable'

dlaliberte commented 7 years ago

There is no arrayToDataTable reference in the loader.js code. This error is probably due to calling arrayToDataTable before the charts code has been properly loaded, especially since you mention that it is only failing periodically rather than every time. Could you copy the start of your code, including the google.charts.load() call and your google.charts.setOnLoadCallback() call?

PaulYoum commented 7 years ago

here's my code snippet:

function loadTwoHourTemperatures(resultFeatures) {
    google.charts.load('current', { 'packages': ['corechart'] });
    google.charts.setOnLoadCallback(drawVisualization);

    var jsonQueryResult;

    function drawVisualization() {

        // Temperature sensors
        var allSensors = [];
        var sensorTime = new Date();

        for (i = 0; i < resultFeatures.length; i++) {
            var location = [];
            location.push(resultFeatures[i].attributes["RWIS_DISPLAY_NAME"]);
            location.push(resultFeatures[i].attributes["RSTEMP_120MIN"]);
            location.push(resultFeatures[i].attributes["RSTEMP_105MIN"]);
            location.push(resultFeatures[i].attributes["RSTEMP_90MIN"]);
            location.push(resultFeatures[i].attributes["RSTEMP_75MIN"]);
            location.push(resultFeatures[i].attributes["RSTEMP_60MIN"]);
            location.push(resultFeatures[i].attributes["RSTEMP_45MIN"]);
            location.push(resultFeatures[i].attributes["RSTEMP_30MIN"]);
            location.push(resultFeatures[i].attributes["RSTEMP_15MIN"]);
            location.push(resultFeatures[i].attributes["RSTEMP_CURRENT"]);
            location.push((location[1] + location[2] + location[3] + location[4] + location[5] + location[6] + location[7] + location[8] + location[9]) / 9)
            allSensors.push(location);

            // fill sensorTime with last sensor's date/time
            sensorTime = resultFeatures[i].attributes["RWIS_DATETIME"];
        };

        // Create Data Table for Google Chart
        var data = google.visualization.arrayToDataTable([
         ['Location', "120 Min Ago", "105 Min Ago", "90 Min Ago", '75 Min Ago', '60 Min Ago', '45 Min Ago', '30 Min Ago', '15 Min Ago', 'Current', "Average"],
         allSensors[0],
         allSensors[1],
         allSensors[2],
         allSensors[3],
         allSensors[4],
         allSensors[5],
         allSensors[6],
         allSensors[7],
         allSensors[8],
         allSensors[9]
        ]);

        var options = {
            vAxis: { title: 'Temperature' },
            hAxis: { textPosition: "out", slantedText: true },
            seriesType: 'bars',
            series: { 9: { type: 'line' }, },
            height: 700,
            width: 950
        };

        var chart = new google.visualization.ComboChart(document.getElementById('two_hour_chart_div'));
        chart.draw(data, options);

    }

    // Force chart to redraw upon resize
    $(window).resize(function () {
        drawVisualization();
    });
}
dlaliberte commented 7 years ago

I would bet your problem is with the last part:

// Force chart to redraw upon resize
$(window).resize(function () {
    drawVisualization();
});

This will be called any time the window is resized, even if the library has not yet finished loading.

I can see that it is easier to just set up this call at the top level, but you can did like this instead (replacing your top level setOnLoadCallback(drawVisualization)):

google.charts.setOnLoadCallback(function() {
  // Force chart to redraw upon resize
  $(window).resize(function () {
    drawVisualization();
  });

  drawVisualization();

});

PaulYoum commented 7 years ago

Unfortunately that optimization hasn't fixed the error. I left my curried function "drawVisualization" still defined in the original function loadTwoHourTemperatures:

function loadTwoHourTemperatures(resultFeatures) {
    google.charts.load('current', { 'packages': ['corechart'] });
    google.charts.setOnLoadCallback(function () {
        // Force chart to redraw upon resize
        $(window).resize(function () {
            drawVisualization();
        });
        drawVisualization();
    });

    var jsonQueryResult;

    function drawVisualization() {

        // Temperature sensors
        var allSensors = [];
        var sensorTime = new Date();

        for (i = 0; i < resultFeatures.length; i++) {
            var location = [];
            location.push(resultFeatures[i].attributes["RWIS_DISPLAY_NAME"]);
            location.push(resultFeatures[i].attributes["RSTEMP_120MIN"]);
            location.push(resultFeatures[i].attributes["RSTEMP_105MIN"]);
            location.push(resultFeatures[i].attributes["RSTEMP_90MIN"]);
            location.push(resultFeatures[i].attributes["RSTEMP_75MIN"]);
            location.push(resultFeatures[i].attributes["RSTEMP_60MIN"]);
            location.push(resultFeatures[i].attributes["RSTEMP_45MIN"]);
            location.push(resultFeatures[i].attributes["RSTEMP_30MIN"]);
            location.push(resultFeatures[i].attributes["RSTEMP_15MIN"]);
            location.push(resultFeatures[i].attributes["RSTEMP_CURRENT"]);
            location.push((location[1] + location[2] + location[3] + location[4] + location[5] + location[6] + location[7] + location[8] + location[9]) / 9)
            allSensors.push(location);

            // fill sensorTime with last sensor's date/time
            sensorTime = resultFeatures[i].attributes["RWIS_DATETIME"];
        };

        // Create Data Table for Google Chart
        var data = google.visualization.arrayToDataTable([
         ['Location', "120 Min Ago", "105 Min Ago", "90 Min Ago", '75 Min Ago', '60 Min Ago', '45 Min Ago', '30 Min Ago', '15 Min Ago', 'Current', "Average"],
         allSensors[0],
         allSensors[1],
         allSensors[2],
         allSensors[3],
         allSensors[4],
         allSensors[5],
         allSensors[6],
         allSensors[7],
         allSensors[8],
         allSensors[9]
        ]);

        var options = {
            vAxis: { title: 'Temperature' },
            hAxis: { textPosition: "out", slantedText: true },
            seriesType: 'bars',
            series: { 9: { type: 'line' }, },
            height: 700,
            width: 950
        };

        var chart = new google.visualization.ComboChart(document.getElementById('two_hour_chart_div'));
        chart.draw(data, options);

    }
}
dlaliberte commented 7 years ago

If you are loading other Google Charts packages in other places, then the problem might be a conflict between how these multiple load calls happen. Or perhaps the order in which they happen makes a difference.

It would better if you could load all the code at the top level, rather than inside any functions, but I am not sure why that should matter, except perhaps it does for some browsers.

PaulYoum commented 7 years ago

Thanks for that. I have to work on some other tasks for a bit but I'll give that a shot later this week. I've only noticed this issue occurring in IE and not in Firefox/Chrome/Safari.

JohnVLinton commented 6 years ago

I've had the same issue and it's not resolving in IE11 (sometimes it's fine in Chrome, sometimes not). Seems to be a bug with Google Charts. I tried to use version 43 or 44 per some recommendations but still get the screen redraw only sporadically unless I do a cache refresh with CTRL + F5. Regular screen load will not draw most of the graphs.

dlaliberte commented 6 years ago

There could always be a bug in Google Charts, but you'll have to point us at a web page or give us enough code to be able to reproduce the problem before we can guess what is going on.

JohnVLinton commented 6 years ago

This is my setup:

  var chartsLoaded = false;

  // Load the Visualization API and the corechart package.
  google.charts.load('45', { 'packages': ['corechart'], 'callback': turnOn});

  // Set a callback to run when the Google Visualization API is loaded.
  google.charts.setOnLoadCallback(turnOn);

  function turnOn() {
      chartsLoaded = true;
      CHARTS.DrawPie();
      CHARTS.LoadVolume();
      CHARTS.OnTime();
      CHARTS.BRLoad();
      CHARTS.LoadCreation();
      CHARTS.BillPay();
      CHARTS.TopLanes();
      CHARTS.ALB();
  }

  var CHARTS = function () {
      var DrawPie = function (d) {
          // Create the data table.
          var data;

          if (d != null) {
              data = new google.visualization.DataTable();
              data.addColumn('string', 'Topping');
              data.addColumn('number', 'Slices');
              data.addRows(d);                  
          }

          if (data != null) {
              // Set chart options
              var options = {
                  width: 270,
                  height: 270,
                  pieHole: 0.6,
                  colors: ['#efefef', '#3667CA'],
                  legend: 'none',
                  pieSliceText: '',
                  chartArea: {'width': '80%', 'height': '85%'}
              };

              // Instantiate and draw our chart, passing in some options.
              var chart = new google.visualization.PieChart(document.getElementById('pieDiv'));
              chart.draw(data, options);
          }
      }

      var LoadVolume = function (d) {            
          var data;
          if (d != null) {
              data = google.visualization.arrayToDataTable(d);
          }

          if (data != null) {
              var options = {                      
                  curveType: 'function',
                  legend: { position: 'bottom' },
                  chartArea: {'width': '80%', 'height': '70%'},
              };

              var chart = new google.visualization.LineChart(document.getElementById('loadVolumeDiv'));

              chart.draw(data, options);
          }
      }

      var OnTime = function (d) {            
          var data;
          if (d != null) {
              data = google.visualization.arrayToDataTable(d);
          }

          if (data != null) {
              var options = {                      
                  curveType: 'function',
                  legend: { position: 'bottom' },
                  chartArea: {'width': '80%', 'height': '70%'},
              };

              var chart = new google.visualization.LineChart(document.getElementById('onTimeDiv'));

              chart.draw(data, options);
          }
      }

      var BRLoad = function (d) {
          var data;
          if (d != null) {
              data = google.visualization.arrayToDataTable(d);
          }

          if (data != null) {
               var view = new google.visualization.DataView(data);
                  view.setColumns([0, 1]);

                  var options = {                       
                      bar: { groupWidth: "50%" },
                      vAxis: { "title": "USD" },
                      hAxis: { "title": "Weeks" },
                      legend: 'none',
                      chartArea: {'width': '80%', 'height': '70%'}
                  };
                  var chart = new google.visualization.ColumnChart(document.getElementById("brloadDiv"));
                  chart.draw(view, options);
          }
      }

      var LoadCreation = function (d) {
          var data;
          if (d != null) {
              data = google.visualization.arrayToDataTable(d);
          }

          if (data != null) {
               var view = new google.visualization.DataView(data);
                  view.setColumns([0, 1, 2]);

                  var options = {                       
                      bar: { groupWidth: "50%" },
                      vAxis: { title: "# of loads" },
                      hAxis: { title: "Day" },
                      legend: { position: 'top', alignment: 'start' }
                  };
                  var chart = new google.visualization.ColumnChart(document.getElementById("loadcreationDiv"));
                  chart.draw(view, options);
          }
      }

      var BillPay = function (d) {
          var data;
          if (d != null) {
              data = google.visualization.arrayToDataTable(d);
          }

          if (data != null) {
               var view = new google.visualization.DataView(data);
                  view.setColumns([0, 1]);

                  var options = {
                      hAxis: {title: 'Weeks',  titleTextStyle: {color: '#333'}},
                      vAxis: { title: 'USD', minValue: 0 },
                      legend: { position: 'top', alignment: 'start' },
                      colors: ['#6FBDE2', '#1594CE'],
                      areaOpacity: 0.8,
                      seriesType: "area"
                    };

                    var chart = new google.visualization.AreaChart(document.getElementById('billpayDiv'));
                    chart.draw(data, options);
          }
      }

      var TopLanes = function (d) {
          var data;
          if (d != null) {
              data = google.visualization.arrayToDataTable(d);
          }

          if (data != null) {
               var view = new google.visualization.DataView(data);
                  view.setColumns([0, 1, 2]);

                  var options = {
                      hAxis: {title: 'Weeks',  titleTextStyle: {color: '#333'}},
                      vAxis: { title: '# of laods', minValue: 0 },
                      legend: { position: 'top', alignment: 'start' },
                      colors: ['#1594CE', '#6FBDE2', '#EEEEEE'],
                      areaOpacity: 1.0,
                      seriesType: "area",
                      isStacked: true,
                      chartArea: {'width': '90%', 'height': '70%'}
                    };

                    var chart = new google.visualization.AreaChart(document.getElementById('toplanesDiv'));
                    chart.draw(data, options);
          }
      }

      var ALB = function (d) {
          var data;
          if (d != null) {
              data = google.visualization.arrayToDataTable(d);
          }

          if (data != null) {
              var view = new google.visualization.DataView(data);
              var options = {
                    legend: { position: 'bottom', maxLines: 3, textStyle: {fontSize: 14} },
                    bar: { groupWidth: '20%' },
                    isStacked: true,
                    title: '15% OVER TERMS',
                    //titleTextStyle: { color: '#9FACBC', fontName: 'Times New Roman', fontSize: 32, bold: true },
                    colors: ['#FD9724', '#043862'],
                    vAxis: { baselineColor: 'none', gridlines: { color: 'transparent' }, textPosition: 'none' },
                    hAxis: { baselineColor: 'none', gridlines: { color: 'transparent' }, textPosition: 'none' },
                    chartArea: { 'width': '96%', 'height': '70%' },
                    annotations: { alwaysInside: true }
              };

              var chart = new google.visualization.BarChart(document.getElementById("albDiv"));
              chart.draw(view, options);
          }
      }

      return {
          DrawPie: DrawPie,
          LoadVolume: LoadVolume,
          OnTime: OnTime,
          BRLoad: BRLoad,
          LoadCreation: LoadCreation,
          BillPay: BillPay,
          TopLanes: TopLanes,
          ALB: ALB
      }
  }();
dlaliberte commented 6 years ago

That code looks fine, but you have left out how you call your chart drawing functions since nothing will draw without providing data via the function parameter d. So this is not enough for us to guess what is going on. And I suspect you have an event handler (or equivalent) that calls one of these functions before charts have finished loading, assuming you are getting the same error reported above: Object doesn't support property or method 'arrayToDataTable'

PaulYoum commented 6 years ago

To help with the issue, @dlaliberte was correct in helping me with my problem. I changed the load order, and my other packages didn't complain, and got my google charts to work.

jvlinton commented 6 years ago

So maybe the issue is I am calling those from within a React ComponentDidMount as the success function when the ajax call returns. Each Ajax call populates the corresponding graph by calling into the above class. I'm not sure, but maybe that's the issue.

Not sure how I would remedy it. I guess I could try populating some data store on the page instead of calling the chart functions as they may be getting called too early. Then have those functions graph based on the data.

I thought my (data != null) checking would detect but I think what I really need is (data != null && chartsLoaded = true).

Will post again if this does not work thanks everyone.

JohnVLinton commented 6 years ago

So I figured out the logic and a friend later said I had basically "manually implemented a promise". I post my fix here in case it will help anyone in a similar situation.

Basically my AJAX that populates the data for each chart calls each function (e.g. LoadVolume) with data that gets set for that particular graph. But the graph itself does not get drawn unless both: a) graph data has been populated; b) charts has been loaded.

The main nugget to ensure that below is: if (data_volume != null && chartsLoaded) {...}

  var chartsLoaded = false;

  // Load the Visualization API and the corechart package.
  google.charts.load('45', { 'packages': ['corechart'], 'callback': turnOn});

  // Set a callback to run when the Google Visualization API is loaded.
  //google.charts.setOnLoadCallback(turnOn);

  var data_pie = null;
  var data_volume = null;
  var data_ontime = null;
  var data_brload = null;
  var data_loadcreation = null;
  var data_billpay = null;
  var data_toplanes = null;
  var data_alb = null;

  function turnOn() {
      chartsLoaded = true;
      CHARTS.DrawPie();
      CHARTS.LoadVolume();
      CHARTS.OnTime();
      CHARTS.BRLoad();
      CHARTS.LoadCreation();
      CHARTS.BillPay();
      CHARTS.TopLanes();
      CHARTS.ALB();
  }

  var CHARTS = function () {
      var DrawPie = function (d) {
          // Create the data table.

          if (d != null) {
              data_pie = d;
          }

          if (data_pie != null && chartsLoaded) {

              var da = new google.visualization.DataTable();
              da.addColumn('string', 'Topping');
              da.addColumn('number', 'Slices');
              da.addRows(data_pie);                  

              // Set chart options
              var options = {
                  width: 270,
                  height: 270,
                  pieHole: 0.6,
                  colors: ['#efefef', '#3667CA'],
                  legend: 'none',
                  pieSliceText: '',
                  chartArea: {'width': '80%', 'height': '85%'}
              };

              // Instantiate and draw our chart, passing in some options.
              var chart = new google.visualization.PieChart(document.getElementById('pieDiv'));
              chart.draw(da, options);
          }
      }

      var LoadVolume = function (d) {            

          if (d != null) {
              data_volume = d;
          }

          if (data_volume != null && chartsLoaded) {

              var da = google.visualization.arrayToDataTable(data_volume);

              var options = {                      
                  curveType: 'function',
                  legend: { position: 'bottom' },
                  chartArea: {'width': '80%', 'height': '70%'},
              };

              var chart = new google.visualization.LineChart(document.getElementById('loadVolumeDiv'));

              chart.draw(da, options);
          }
      }

      var OnTime = function (d) {            

          if (d != null) {
              date_ontime = d;
          }

          if (date_ontime != null && chartsLoaded) {
              var da = google.visualization.arrayToDataTable(date_ontime);
              var options = {                      
                  curveType: 'function',
                  legend: { position: 'bottom' },
                  chartArea: {'width': '80%', 'height': '70%'},
              };

              var chart = new google.visualization.LineChart(document.getElementById('onTimeDiv'));

              chart.draw(da, options);
          }
      }

      var BRLoad = function (d) {

          if (d != null) {
              data_brload = d;
          }

          if (data_brload != null && chartsLoaded) {

              var da = google.visualization.arrayToDataTable(data_brload);

               var view = new google.visualization.DataView(da);
                  view.setColumns([0, 1]);

                  var options = {                       
                      bar: { groupWidth: "50%" },
                      vAxis: { "title": "USD" },
                      hAxis: { "title": "Weeks" },
                      legend: 'none',
                      chartArea: {'width': '80%', 'height': '70%'}
                  };
                  var chart = new google.visualization.ColumnChart(document.getElementById("brloadDiv"));
                  chart.draw(view, options);
          }
      }

      var LoadCreation = function (d) {

          if (d != null) {
              data_loadcreation = d;
          }

          if (data_loadcreation != null && chartsLoaded) {

               var da = google.visualization.arrayToDataTable(data_loadcreation);

               var view = new google.visualization.DataView(da);
                  view.setColumns([0, 1, 2]);

                  var options = {                       
                      bar: { groupWidth: "50%" },
                      vAxis: { title: "# of loads" },
                      hAxis: { title: "Day" },
                      legend: { position: 'top', alignment: 'start' }
                  };
                  var chart = new google.visualization.ColumnChart(document.getElementById("loadcreationDiv"));
                  chart.draw(view, options);
          }
      }

      var BillPay = function (d) {

          if (d != null) {
              data_billpay = d;
          }

          if (data_billpay != null && chartsLoaded) {

               var da = google.visualization.arrayToDataTable(data_billpay);

               var view = new google.visualization.DataView(da);
                  view.setColumns([0, 1]);

                  var options = {
                      hAxis: {title: 'Weeks',  titleTextStyle: {color: '#333'}},
                      vAxis: { title: 'USD', minValue: 0 },
                      legend: { position: 'top', alignment: 'start' },
                      colors: ['#6FBDE2', '#1594CE'],
                      areaOpacity: 0.8,
                      seriesType: "area"
                    };

                    var chart = new google.visualization.AreaChart(document.getElementById('billpayDiv'));
                    chart.draw(da, options);
          }
      }

      var TopLanes = function (d) {

          if (d != null) {
              data_toplanes = d;
          }

          if (data_toplanes != null && chartsLoaded) {

              var da = google.visualization.arrayToDataTable(data_toplanes);

               var view = new google.visualization.DataView(da);
                  view.setColumns([0, 1, 2]);

                  var options = {
                      hAxis: {title: 'Weeks',  titleTextStyle: {color: '#333'}},
                      vAxis: { title: '# of laods', minValue: 0 },
                      legend: { position: 'top', alignment: 'start' },
                      colors: ['#1594CE', '#6FBDE2', '#EEEEEE'],
                      areaOpacity: 1.0,
                      seriesType: "area",
                      isStacked: true,
                      chartArea: {'width': '90%', 'height': '70%'}
                    };

                    var chart = new google.visualization.AreaChart(document.getElementById('toplanesDiv'));
                    chart.draw(da, options);
          }
      }

      var ALB = function (d) {

          if (d != null) {
              data_alb = d;
          }

          if (data_alb != null && chartsLoaded) {

              var da = google.visualization.arrayToDataTable(data_alb);

              var view = new google.visualization.DataView(da);
              var options = {
                    legend: { position: 'bottom', maxLines: 3, textStyle: {fontSize: 14} },
                    bar: { groupWidth: '20%' },
                    isStacked: true,
                    title: '15% OVER TERMS',
                    //titleTextStyle: { color: '#9FACBC', fontName: 'Times New Roman', fontSize: 32, bold: true },
                    colors: ['#FD9724', '#043862'],
                    vAxis: { baselineColor: 'none', gridlines: { color: 'transparent' }, textPosition: 'none' },
                    hAxis: { baselineColor: 'none', gridlines: { color: 'transparent' }, textPosition: 'none' },
                    chartArea: { 'width': '96%', 'height': '70%' },
                    annotations: { alwaysInside: true }
              };

              var chart = new google.visualization.BarChart(document.getElementById("albDiv"));
              chart.draw(view, options);
          }
      }

      return {
          DrawPie: DrawPie,
          LoadVolume: LoadVolume,
          OnTime: OnTime,
          BRLoad: BRLoad,
          LoadCreation: LoadCreation,
          BillPay: BillPay,
          TopLanes: TopLanes,
          ALB: ALB
      }
  }();