1

I am trying to use google charts in my Angular web app and am having problems fetching the data from a controller. Consider the following attempt at a google chart component. When I use the commented out google.visualization data table code, the component works. I have a controller set up to return similar data from a server. An example of the returned data I show following the code. When I attempt to use the fetched data the chart component fails, it just produces nothing. My attempts at debugging show that my understanding of HttpClient is very poor. Consider the two http.get statements at the top of ngOnInit; the first shows console output for the response object that looks very much like I expect for an object created from the json returned by the controller. The second http.get results in this.chartData being undefined, which at the moment appears to be the heart of the problem. Any hints?

import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';

declare var google: any;

// Reference:
//  https://stackoverflow.com/questions/37542408/angular2-google-charts-how-to-integrate-google-charts-in-angular2

@Component({
selector: 'google-chart',
templateUrl: './google-chart.component.html',
styleUrls: ['./google-chart.component.css']
})
export class GoogleChartComponent implements OnInit {
    private chartData: any;

constructor(
    private http: HttpClient) {
}

ngOnInit() {

    this.http.get('api/BinomialResult/GetGoogleChartData')
        .subscribe(response => console.log("response:",response));

    this.http.get('api/BinomialResult/GetGoogleChartData')
        .subscribe(response => this.chartData = response);

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

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

    // Callback that creates and populates a data table, 
    // instantiates the chart, passes in the data and
    // draws it.
    function drawChart(this: any) {

        // (1) Create the data table from an array.

        //var dataTable = google.visualization.arrayToDataTable([
        //  ['X', 'Prior', 'Posterior'],
        //  [0.0, 5.000, 0.000],
        //  [0.1, 1.061, 0.026],
        //  [0.2, 0.795, 0.347],
        //  [0.3, 0.694, 1.180],
        //  [0.4, 0.649, 2.152],
        //  [0.5, 0.636, 2.586],
        //  [0.6, 0.649, 2.152],
        //  [0.7, 0.694, 1.180],
        //  [0.8, 0.795, 0.347],
        //  [0.9, 1.061, 0.026],
        //  [1.0, 5.000, 0.000]
        //]);

        // (2) Create the data table explicitly

        //var dataTable = new google.visualization.DataTable();
        //var newData = [
        //  ['X', 'Prior', 'Posterior'],
        //  [   0.0,  10.07,  4.169E-11 ],
        //  [   0.1,  1.061,  0.026 ],
        //  [   0.2,  0.795,  0.347 ],
        //  [   0.3,  0.694,  1.180 ],
        //  [   0.4,  0.649,  2.152 ],
        //  [   0.5,  0.636,  2.586 ],
        //  [   0.6,  0.649,  2.152 ],
        //  [   0.7,  0.694,  1.180 ],
        //  [   0.8,  0.795,  0.347 ],
        //  [   0.9,  1.061,  0.026 ],
        //  [   1.0,  10.07,  4.169E-11 ]
        //];

        //// determine the number of rows and columns.
        //var numRows = newData.length;
        //var numCols = newData[0].length;

        //// addd the columns
        //for (var i = 0; i < numCols; i++)
        //  dataTable.addColumn('number', newData[0][i]);

        //// add the rows.
        //for (var i = 1; i < numRows; i++)
        //  dataTable.addRow(newData[i]);           

        // (3) Create the data table from json

        var dataTable = new google.visualization.arrayToDataTable(this.chartData);

        // Set chart options

        var options = {
            title: 'Google Line Chart Example',
            width: 600,
            height: 370,
            chartArea: { left: 40, top: 30},
            curveType: 'none',
            hAxis: {
                title: 'P\n\n\n\n',  // https://www.webtoolhub.com/tn561380-xhtml-characters-list.aspx?type=script&category=greek-coptic
                textStyle: { 
                    //color: '#01579b',
                    //fontSize: 20,
                    fontName: 'Arial',
                    bold: false,
                    italic: false
                },
                titleTextStyle: {
                    //color: '#01579b',
                    //fontSize: 16,
                    fontName: 'Arial',
                    bold: false,
                    italic: false
                }
            },
            vAxis: {
                title: 'Likelihood',
                textStyle: {
                    //color: '#1a237e',
                    //fontSize: 24,
                    bold: false,
                    italic: false
                },
                titleTextStyle: {
                    //color: '#1a237e',
                    //fontSize: 24,
                    bold: false,
                    italic: false
                }
            },
        };

        // Instantiate and draw our chart, passing in some options.
        var chart = new google.visualization.LineChart(document.getElementById('chartDiv'));

        chart.draw(dataTable, options);
    }
  }
}

Here is the json data returned by the controller:

{
  "cols": [
    {"type": "number" ,"id": "X" ,"label": "X" }, 
    {"type": "number" ,"id": "Prior" ,"label": "Prior" }, 
    {"type": "number" ,"id": "Posterior" ,"label": "Posterior" }
    ],
  "rows" : [
    {"c" : [{"v": 0}, {"v": 10.0708791199471}, {"v": 4.16959810659944E-11}]}, 
    {"c" : [{"v": 0.1}, {"v": 1.06103295394597}, {"v": 0.0260699856599757}]}, 
    {"c" : [{"v": 0.2}, {"v": 0.795774715459477}, {"v": 0.347207759022947}]}, 
    {"c" : [{"v": 0.3}, {"v": 0.694609118042857}, {"v": 1.18041936646279}]}, 
    {"c" : [{"v": 0.4}, {"v": 0.649747334361397}, {"v": 2.15278216848928}]}, 
    {"c" : [{"v": 0.5}, {"v": 0.636619772367581}, {"v": 2.58689939247778}]}, 
    {"c" : [{"v": 0.6}, {"v": 0.649747334361397}, {"v": 2.15278216848928}]}, 
    {"c" : [{"v": 0.7}, {"v": 0.694609118042856}, {"v": 1.18041936646279}]}, 
    {"c" : [{"v": 0.8}, {"v": 0.795774715459477}, {"v": 0.347207759022947}]}, 
    {"c" : [{"v": 0.9}, {"v": 1.06103295394597}, {"v": 0.0260699856599757}]}, 
    {"c" : [{"v": 1}, {"v": 10.0708791199471}, {"v": 4.16959810659946E-11}]}
    ]
}

I have applied line breaks and indented to make the returned data easier to read. The json data returned was generated using the Google.DataTable.Net.Wrapper for C# that is available as a Nuget package.

WhiteHat
  • 59,912
  • 7
  • 51
  • 133
John Schroeder
  • 85
  • 1
  • 10
  • the code doesn't recognize `this` here --> `this.http` – WhiteHat Nov 26 '19 at 19:34
  • Have updated the code without understanding the issue you identified (why this.http was undefined). My difficulties with HttpClient are not solved, just moved to next, so to speak. I revised my question to point to the new sticking point. – John Schroeder Nov 27 '19 at 16:24

2 Answers2

1

the chart code may be running before the data has been assigned to this.chartData.

you should probably include the chart code inside the subsribe function.

try the following structure...

import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';

declare var google: any;

// Reference:
//  https://stackoverflow.com/questions/37542408/angular2-google-charts-how-to-integrate-google-charts-in-angular2

@Component({
  selector: 'google-chart',
  templateUrl: './google-chart.component.html',
  styleUrls: ['./google-chart.component.css']
})

export class GoogleChartComponent implements OnInit {
  private chartData: any;

  constructor(
    private http: HttpClient) {
  }

  ngOnInit() {
    this.http.get('api/BinomialResult/GetGoogleChartData').subscribe(function (response) {
      google.charts.load('current', {
        packages: ['corechart']
      }).then(function () {
          var dataTable = new google.visualization.DataTable(response);

          var options = {
              title: 'Google Line Chart Example',
              width: 600,
              height: 370,
              chartArea: { left: 40, top: 30},
              curveType: 'none',
              hAxis: {
                  title: 'P\n\n\n\n',
                  textStyle: {
                      fontName: 'Arial',
                      bold: false,
                      italic: false
                  },
                  titleTextStyle: {
                      fontName: 'Arial',
                      bold: false,
                      italic: false
                  }
              },
              vAxis: {
                  title: 'Likelihood',
                  textStyle: {
                      bold: false,
                      italic: false
                  },
                  titleTextStyle: {
                      bold: false,
                      italic: false
                  }
              },
          };

          var chart = new google.visualization.LineChart(document.getElementById('chartDiv'));
          chart.draw(dataTable, options);
      });
    });
  }
}

note: the json you're using will create a data table directly,
don't need the helper method arrayToDataTable

var dataTable = new google.visualization.DataTable(response);
WhiteHat
  • 59,912
  • 7
  • 51
  • 133
  • Yes, that is the problem. I had come up with a similar solution and when I came back here to post, you had already answered. – John Schroeder Nov 29 '19 at 02:08
  • The approach I posted above worked fine until I upgraded to Angular 8 and dotnet sdk 3.1. I have no idea why, but mine broke and your approach is still working. So I have accepted your answer. This raises the larger question of how to build a useful Google chart library that avoids this loading issue. Have not found an example that works with async data loading. My own attempts are still flaky, working only after the initial load. If you come across a good example, please point me to it. – John Schroeder Dec 10 '19 at 17:35
0

WhiteHat found the problem. Yeah! I came up with something similar. The trick was to not just assign data in the subscribe callback, but to also not give google charts the drawChart function before the data was available or risk the dreaded no columns error message. Pretty much as WhiteHat has it.

Here is the code that I settled on:

import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';

declare var google: any;

@Component({
selector: 'google-chart',
templateUrl: './google-chart.component.html',
styleUrls: ['./google-chart.component.css']
})
export class GoogleChartComponent implements OnInit {

public chartData: any;
public dataTable: any;

constructor(
    private http: HttpClient) {
}

ngOnInit() {

    google.charts.load('current', { 'packages': ['corechart'] });

    this.http.get('api/BinomialResult/GetGoogleChartData')
        .subscribe((response: any) => {
            this.chartData = response;
            this.dataTable = new google.visualization.DataTable(this.chartData);
            google.charts.setOnLoadCallback(this.drawChart.bind(this));
        });
}
private drawChart(this: any) {

    // Set chart options
    var options = {
        title: 'Google Line Chart Example',
        width: 600,
        height: 370
    };

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

    chart.draw(this.dataTable, options);
  }

}
John Schroeder
  • 85
  • 1
  • 10