5

This way my histogram works fine,when I load it like during page load.

$(document).ready()
{
x = new Array(10);
for (var i = 0; i < 10; i++) {
    x[i] = new Array(2);
    x[i][0]="txt";
    x[i][1]=100;

}
loadChart2(x);
}

Google column chart code: (adopted from Google charting api)

function loadChart2 (histValues)
{
    console.log('histValues:'+histValues);
    google.load("visualization", "1", {packages:["corechart"]});
    google.setOnLoadCallback(drawChart);
    function drawChart() {
        var data = google.visualization.arrayToDataTable(histValues);

        var options = {
            hAxis: {title: 'Score', titleTextStyle: {color: 'red'}}
        };

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

My problem:

But when I call loadChart2() from inside my angularJS controller, entire screen becomes white and no error is shown in browser console.

$http({method: 'GET', url: urlGetHistogramData, timeout: maxTime})
            .success(function (data) {
            x=new Array(data.score_hist.length);
            var i=0;
            $.each(data.score_hist, function(){
                x[i] = new Array(2);
                x[i][0]=this.interval_start;
                x[i][1]=this.count;
                i++;
                });


                loadChart2(x);

});

Debugging info:

I can see in console that interval_start and count values are printed, so service is returning values fine

Also, I can see histValues expectantly printed on console from loadChart() function.

Here is a related question, in case you want more in-depth details How to create a historgram from json

As soon as I put loadChart2() function in any AngularJS onClick or any function, I get total white screen with no error in console. It seems, none has any comment on my problem, I will keep this page updated with my findings on this.

Edit I am pursuing a solution to this problem, I asked a question related to this issue https://stackoverflow.com/questions/16816782/how-set-charts-data-in-google-chart-tool-directive-module-from-a-dynamically-po

Community
  • 1
  • 1
Watt
  • 3,118
  • 14
  • 54
  • 85
  • 1
    Sounds like you're trying to manipulate the DOM at the wrong point in the execution stack. I wonder what results you'd get if you called `loadChart2()` from within a directive. – Steven Maitlall May 23 '13 at 20:21
  • 1
    +1.You are right about manipulating DOM (I did as last resort due to limitation with Google graphing API). Maybe using directive would be way to do it. I am new to AngularJS don't know much about Directive, can you please give some clue, how to use Directive in this case. Thanks!! – Watt May 23 '13 at 20:38
  • so? did you try bootstrapping angular in the google.setOnLoadCallback as I already explained you? because trying to load the charting library after bootstrapping angular is the cause of that weird white page error. So, the white stuff -> bad charting library load, the "Not an array" error -> whatever you are passing isn't an array (try to test it with Array.isArray). – coma May 31 '13 at 20:52

4 Answers4

9

The key is to manual bootstraping your Angular module after the Google charting library load:

http://jsfiddle.net/nCFd6/22/

App

var app = angular.module('app', []);

Directive

app.directive('chart', function() {

    return {
        restrict: 'E',
        replace: true,
        scope: {
            data: '=data'
        },
        template: '<div class="chart"></div>',
        link: function(scope, element, attrs) {

            var chart = new google.visualization.LineChart(element[0]);
            var options = {};

            scope.$watch('data', function(v) {

                var data = google.visualization.arrayToDataTable(v);
                chart.draw(data, options);

            });

        }
    };

});

Controller

app.controller('ChartController', function($scope) {

    $scope.scoreHistory = [];
    $scope.loadDataFromServer = function() {

        var x = [
            ['interval', 'count']
        ];

        var scoreHistory = [
            {
                intervalStart: 12,
                count: 20
            },
            {
                intervalStart: 100,
                count: 200
            },
            {
                intervalStart: 200,
                count: 50
            },
            {
                intervalStart: 250,
                count: 150
            }
        ];

        angular.forEach(scoreHistory, function(record, key) {

            x.push([
                record.intervalStart,
                record.count
            ]);

        });

        $scope.scoreHistory = x;

    };

});

Vodoo

google.setOnLoadCallback(function() {

    angular.bootstrap(document, ['app']);

});

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

View

<div ng-controller="ChartController">
    <button ng-click="loadDataFromServer()">load data</button>
    <chart data="scoreHistory"></chart>
</div>

As you can see in this example, I've made a chart directive so you can reuse it.

coma
  • 16,429
  • 4
  • 51
  • 76
  • Thanks! Going to try this one in my project – Watt May 29 '13 at 16:19
  • I've added a "click" to simulate your ajax callback. – coma May 29 '13 at 16:33
  • I am trying to debug following error I get while trying your code in my project `Error: Not an array at Error () at Object.b [as arrayToDataTable] (https://www.google.com/uds/api/visualization/1.0/e0ebcb36e2905f30852752301c93f500/format+en,default,corechart.I.js:357:85)` – Watt May 29 '13 at 17:08
  • That's because your array is no well formed. I've updated my answer to satisfy your needs, take a look. – coma May 29 '13 at 17:52
  • I think there might be something else too going on. Because I copy pasted your fiddle code in a separate tmp.html and tmp.js file, it ran fine without any issue. But, as soon as I bring it in my .js file which has other angular controller as well.. I start to see this error. Hopefully, I will figure out what is happening soon. – Watt May 29 '13 at 18:11
  • Well, just try to get the line where the arrayToDataTable gets called and verify the value you are passing to it with console.log() (compare it with this http://jsfiddle.net/coma/nCFd6/21/ using Firebug or Chrome JS console). But is my answer right?, if so... accept it! – coma May 29 '13 at 18:15
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/30855/discussion-between-coma-and-watt) – coma May 29 '13 at 18:17
  • Sorry for delay in giving bounty. Actually, I was learning more AngularJS and just now finished reading directive/module/Service and hence able to implement your solution. – Watt Jun 03 '13 at 19:17
  • Hey @Watt!, check this out http://www.egghead.io . Would you accept this as the correct answer? – coma Jun 03 '13 at 19:54
  • Fixed! I forgot to add the replace property. – coma Jun 03 '13 at 21:42
2

Here's an alternative that provides some flexibility in the event that you have already defined a module created at the root of the document (the <html> tag) and you have some controllers and directives already set up.

In application.js

var app = angular.module("chartApp", []);

app.controller("ChartCtrl", function($scope) {

 $scope.buildTable = function() {
    var dataTable = new google.visualization.DataTable();
    dataTable.addColumn('string', 'Name');
    dataTable.addColumn('string', 'Address');

    var options = {showRowNumber: true};

    var table = new google.visualization.Table(angular.element("div#chart")[0]);

    var rows = [...]; //Table data here

    dataTable.addRows(rows);

    table.draw(dataTable, options);
 }

//Other existing controllers and directives

In your index.html

<div id="container" ng-controller="ChartCtrl">
 <div id="chart">Loading...</div>
</div>

<script type="text/javascript" src="angular.js"></script>
<script type="text/javascript" src="application.js"></script>
<script type="text/javascript" src="google/charts/api"></script>
<script type="text/javascript">
 google.setOnLoadCallback(function() {
            angular.element("div#container" ).scope( ).buildTable();
        });
 google.load('visualization', '1', {packages:['table']});
</script>

With this approach, it takes away the complexity of communicating between angular modules if the Google Charts component is just a feature of a much bigger application that you already have going.

kshep92
  • 841
  • 1
  • 11
  • 22
1

I'm still making my way through the learning process of AngularJS too. This (https://stackoverflow.com/a/15012542/2406758) blew my mind today and I think I'll make a lot more progress in the coming weeks.

It's an excellent companion to the AngularJS tutorials and explains Services, Controllers and Directives, in a way that I haven't found anywhere else. There are some good Directive examples too, to help with understanding what he's explaining.

I'm not entirely sure this will turn out to be the answer to your problem, but it's a good place to start.

I expect you'll end up with something like

<div id='chart' chart-view>

.directive( 'chartView', function ( ) {
  return {
    scope: true,
    link: function ( scope, element, attrs ) {
      x = new Array(10);
      for (var i = 0; i < 10; i++) {
          x[i] = new Array(2);
          x[i][0]="txt";
          x[i][1]=100;
       }
       loadChart2(x);
    }
  };
});
Community
  • 1
  • 1
Steven Maitlall
  • 777
  • 3
  • 5
  • 1
    Thanks! I will try this. BTW, I found another cool tutorial http://www.adobe.com/devnet/html5/articles/angularjs-directives-and-the-computer-science-of-javascript.html – Watt May 23 '13 at 21:22
  • I thoroughly tested it. Unfortunately it didn't work. No chart is rendering on screen when I use directive you provided. +1 for great tutorial and trying though. – Watt May 23 '13 at 21:48
1

You can try below URL:

AngularJs Google Chart Tools directive

http://bouil.github.io/angular-google-chart/

JQuery Guru
  • 6,943
  • 1
  • 33
  • 40