xavier268
xavier268

Reputation: 51

Google charts (jsapi) into angular2 component

I'm struggling to create an angular2 component that would call google chart api and display a google-chart (https://developers.google.com/chart/interactive/docs/quick_start). This usually leads to unexplained mistakes or just the browser loading forever.

EDIT : full updated test files added below

See also in plnkr here.

To be more specific, these are the test files I use to identify the point where things start to go wrong :

index.html

<html>
<head>
<title>Angular 2 QuickStart</title>
<script src="https://code.angularjs.org/tools/system.js"></script>
<script src="https://code.angularjs.org/tools/typescript.js"></script>
<script src="https://code.angularjs.org/2.0.0-alpha.46/angular2.dev.js">    </script>

<!--Load the AJAX API-->
<script type="text/javascript" src="https://www.google.com/jsapi"></script>


<script>
  System.config({
    transpiler: 'typescript',
    typescriptOptions: { emitDecoratorMetadata: true }
  });
  System.import('./app.ts');
</script>
</head>
<body>
<my-app>loading...</my-app>
</body>
</html>

And then, app.ts

import {bootstrap, Component} from 'angular2/angular2';


@Component({
selector: 'my-app',
template:`
<h1>Demo component my-app</h1>
<div id="cc" (click)="test($event)">Click me</div>
`
})
class AppComponent {

constructor() {
console.log("Constructor was called");
}

test(event) {
console.log("Event :",event);
console.log("Test :", google);
console.log("Document object by id",document.getElementById("cc"));

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

  console.log("Callback is entered ");

  // Create the data table.
  var data = new google.visualization.DataTable();
  data.addColumn('string', 'Topping');
  data.addColumn('number', 'Slices');
  data.addRows([
    ['Mushrooms', 3],
    ['Onions', 1],
    ['Olives', 1],
    ['Zucchini', 1],
    ['Pepperoni', 2]
  ]);

  // Set chart options
  var options = {'title':'How Much Pizza I Ate Last Night',
                 'width':400,
                 'height':300};

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



  console.log("Google object : ", google);
  console.log("Now, loading visualization api");
  // Load the Visualization API and the piechart package.
  google.load('visualization', '1.0', {'packages':['corechart']});
  console.log("Now, setting callback");
  // Set a callback to run when the Google Visualization API is loaded.
  google.setOnLoadCallback(drawChart);
  console.log("Callback was set");
  }

}



bootstrap(AppComponent);

And this is what console shows, when I click :

Constructor was called
app.ts:18 Event : MouseEvent {isTrusted: true}
app.ts:19 Test : Object {loader: Object}
app.ts:20 Document object by id <div id=​"cc">​Click me​</div>​
app.ts:53 Google object :  Object {loader: Object}
app.ts:54 Now, loading visualization api
app.ts:57 Now, setting callback
app.ts:60 Callback was set

Clearly, the callback is never entered into ?!

Anyone can help me sort out this issue ? Many thanks !

Upvotes: 2

Views: 6458

Answers (2)

xavier268
xavier268

Reputation: 51

Finally, solved it by using a chart component to call a chart directive. The setter of that directive triggers the drawing. No further need to directly capture/manage drawing events.

  1. Load google script from the top index.html file

    <script type="text/javascript" src="https://www.google.com/jsapi">
    </script>
    <script type="text/javascript">        
      google.load('visualization', '1.0', {
        'packages': ['corechart']
      });
    </script>
    
  2. Create a "chart" directive to draw google chart (chart.directive.ts)

    /**
    *      This directive will draw a chart from the array of records provided
    *
    *           Note : the relevant jsapi scripts should be already available
    *                  globally in the window.google object (see index.html)
    **/
    
    import {Directive, ElementRef, Input} from "angular2/core";
    import {CORE_DIRECTIVES } from "angular2/common";
    
    @Directive({
      selector: "chart",
    })
    export class ChartDirective {
    
      el: HTMLElement; 
      w: any;  // To store the window, without generating errors in typescript on window.google
      private _content: any[] = [];         
    
      // Setter for content will trigger drawing (or refreshing)
      @Input()
      set content(c: any[]) {
        console.log("Setting content ...");
        this._content = c;
        this.draw();
      }
    
      get content() { return this._content; }
    
    
    
      // Constructor inject a ref to the element
      constructor(elementRef: ElementRef) {
      console.log("Constructing chart directive");
      this.w = window;
      this.el = elementRef.nativeElement; // You cannot use elementRef directly !
      // console.log("Native HTML :", this.el);
      if (!this.w.google) { console.error("Hey ! It seems the needed google script was not loaded ?"); };
      }
    
    // Do the actual drawing
    draw() {
      // Create the data table.
      let data = new this.w.google.visualization.DataTable();
      data.addColumn("date", "Quand");
      data.addColumn("number", "KG");
      let rows = [];
      for (let c in this._content) {
        let d: Date = new Date(this._content[c].quand);
        let k: number = +(this._content[c].kg); // Plus sign to force conversion sting -> number
        // Only take into account records after the 'notBefore date'
        if (d >= this._nbd) rows.push([d, k]);
      }
      data.addRows(rows);
      // Create options
      let options: any = {
        // "width": 600,
        "height": 300,
        "curveType": "function"
      };
    
    
      // Instantiate and draw our chart, passing in some options.
      (new this.w.google.visualization.LineChart(this.el))
        .draw(data, options);
    }
    

    }

  3. Use the directive in a chart component (chart.component) ...

    import {Component } from "angular2/core";
    import {CORE_DIRECTIVES } from "angular2/common";
    
    import {MyModel} from "./mymodel.service.ts";
    import {ChartDirective} from "./chart.directive.ts";
    
    
    @Component({
    selector: "chartPage",
    templateUrl: "/components/chart.template.html",
    directives: [CORE_DIRECTIVES, ChartDirective]        
    })
    export class ChartComponent {     
    
    }
    
    // Injecting my data model into chart component ...
    constructor(private model: MyModel) {
      console.log("Constructing chart component");
    };
    

    }

  4. ... using the following template for the chart

    <chart [content]="model.content" ></chart>
    

Upvotes: 2

jparg
jparg

Reputation: 1156

I was having the same problem when trying to implement google charts into ionic2. Iam not sure what the problem is exactly, but it seems that

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

takes too long to load and

  google.setOnLoadCallback(drawChart);

is never executed. You can bypass this by adding the callback directly to the google.load() like this:

    google.load('visualization', '1.0', {'packages':['corechart'], callback: drawChart});

Worked for me! and the charts are loading as desired. All credit for this solution goes to @davidkonrad:

https://stackoverflow.com/a/31040694

Hope it works!

Upvotes: 0

Related Questions