I’ve recently been working on a Rails application with an AngularJS front end. One of the stories required a pie chart, and we chose to use Google Charts via Angular Google Chart.

Using Angular Google Chart’s live demo for reference, it’s pretty easy to put together a controller that displays the chart:

Using JSON
app.controller("SampleCtrl", function ($scope) {
$scope.chart = {
type: "PieChart",
cssStyle: "height:600px; width:100%",
options: { title: "Sales per Month"},
data: {
cols: [
{
id: "month",
label: "Month",
type: "string"
},
{
id: "sales",
label: "Sales",
type: "number"
}
],
rows: [
{
c: [ { v: "January" }, { v: 19 } ]
},
{
c: [ { v: "February" }, { v: 13 } ]
},
{
c: [ { v: "March" }, { v: 24 } ]
}
]
}
};
});

This version of the code, like the sample it’s derived from, uses simple arrays and objects for the data. This works, but I find it a bit hard to read, especially when generating the data rows dynamically from real data that we retrieve from the back end. What do c and v mean? My guess is column and value, but the code gives up some expressiveness in favor of terseness which is rarely a good tradeoff.

Google Charts has a DataTable object that Angular Google Charts understands. We decided to use it:

Using DataTable (broken)
app.controller("SampleCtrl", function ($scope) {
var table = new google.visualization.DataTable();
table.addColumn("string", "Month");
table.addColumn("number", "Sales");
table.addRows([
[ "January", 19 ],
[ "February", 13 ],
[ "March", 24 ]
]);
$scope.chart = {
type: "PieChart",
cssStyle: "height:600px; width:100%",
options: { title: "Sales per Month"},
data: table
};
});

This is a fairly simple transformation that makes the code more readable. However, it doesn’t actually work. Depending on how fast the Google Charts code loads and whether or not it is cached, this code causes one of several different errors to be logged.

We could load the code ourselves before using it, but Angular Google Charts is already doing that, so why duplicate the work? We just need a way to wait until the load is finished before trying to use the DataTable.

Fortunately, Angular Google Charts provides a factory, googleChartApiProxy, for just this purpose. We inject the factory into the controller and then call it with a callback function and a context object. The callback function is called on the context object once the Google Charts library has been fully loaded. In the example below, I’m not using the context object, so I just pass undefined. The factory returns a function that must then be called to trigger the delayed action.

With this updated version, everything works just fine:

Using DataTable (fixed)
app.controller("SampleCtrl", function ($scope, googleChartApiProxy) {
googleChartApiProxy(function () {
var table = new google.visualization.DataTable();
table.addColumn("string", "Month");
table.addColumn("number", "Sales");
table.addRows([
[ "January", 19 ],
[ "February", 13 ],
[ "March", 24 ]
]);
$scope.chart = {
type: "PieChart",
cssStyle: "height:600px; width:100%",
options: { title: "Sales per Month"},
data: table
};
}, undefined)();
});

This code could be cleaned up further, I’m sure, but this shows the basic technique.