Search code examples
javascriptchartspromisegoogle-visualization

Promisify google.charts.setOnLoadCallback


I've been using google's charts API and have reached a dead end. I use the API to query a spreadsheet and return some data. For visualizations I'm using Razorflow - a JS dashboard framework - not Google Charts. Getting the data is pretty straight forward using code like this (this code should work - spreadsheet is public):

function initialize() {
// The URL of the spreadsheet to source data from.
var myKey = "12E2fE8GWuPvXJoiRZgCZUCFhRKlW69uJAm7fch71jhA"
var query = new google.visualization.Query("https://docs.google.com/spreadsheets/d/" + myKey + "/gviz/tq?sheet=Sheet1");
query.setQuery("SELECT A,B,C WHERE A>=1 LIMIT 1");
query.send(function processResponse(response) {

    var KPIData = response.getDataTable();
    var KPIName = [];
    myNumberOfDataColumns = KPIData.getNumberOfColumns(0) - 1;        

        for (var h = 0; h <= myNumberOfDataColumns ; h++) {
        KPIName[h] = KPIData.getColumnLabel(h);
    };  
});
};     
google.charts.setOnLoadCallback(initialize);

The above will create an array holding the column labels for column A,B and C.

Once the data is fetched I want to use the data for my charts. Problem is, I need to have the data ready before I create the charts. One way I have done this, is creating the chart before calling google.charts.setOnLoadCallback(initialize) and then populate the charts with data from inside the callback. Like this:

//create dashboard
StandaloneDashboard(function (db) {
//create chart - or in this case a KPI
var firstKPI = new KPIComponent();
//add the empty component
db.addComponent(firstKPI);
//lock the component and wait for data
firstKPI.lock();


function initializeAndPopulateChart() {
// The URL of the spreadsheet to source data from.
var myKey = "12E2fE8GWuPvXJoiRZgCZUCFhRKlW69uJAm7fch71jhA"
var query = new google.visualization.Query("https://docs.google.com/spreadsheets/d/" + myKey + "/gviz/tq?sheet=Sheet1");
query.setQuery("SELECT A,B,C WHERE A>=1 LIMIT 1");
query.send(function processResponse(response) {

    var KPIData = response.getDataTable();
    var KPIName = [];
    myNumberOfDataColumns = KPIData.getNumberOfColumns(0) - 1;        

        for (var h = 0; h <= myNumberOfDataColumns ; h++) {
        KPIName[h] = KPIData.getColumnLabel(h);
    };
    //use label for column A as header
    firstKPI.setCaption(KPIName[0]);
    //Set a value - this would be from the query too
    firstKPI.setValue(12);
    //unlock the chart
    firstKPI.unlock();  
});
};     
google.charts.setOnLoadCallback(initializeAndPopulateChart);
});

It works but, I would like to separate the chart functions from the data loading. I guess the best solution is to create a promise. That way I could do something like this:

//create dashboard
StandaloneDashboard(function (db) {

function loadData() {
return new Promise (function (resolve,reject){
    //get the data, eg. google.charts.setOnLoadCallback(initialize);
})
}

loadData().then(function () {
    var firstKPI = new KPIComponent();
    firstKPI.setCaption(KPIName[0]);
    firstKPI.setValue(12);
    db.addComponent(firstKPI); 
    })
});

As should be quite obvious, I do not fully understand how to use promises. The above does not work but. I have tried lots of different ways but, I do not seem to get any closer to a solution. Am I on the right track in using promises? If so, how should i go about this?


Solution

  • Inside a promise you need to call resolve or reject function when async job is done.

    function loadData() {
      return new Promise (function (resolve,reject){
        query.send(function() {
          //...
          err ? reject(err) : resolve(someData);
        });
      })
    }
    

    And then you can do

    loadData().then(function (someData) {
        //here you can get async data
      }).catch(function(err){
        //here you can get an error
      });
    });