0

Angular noob here. I'm building an app that needs to poll a url every second, and store that data persistently, as it needs to be accessed by multiple views/controllers.

What I've done to handle this is put my http request into a factory, and the data is made available to controllers through functions in the factory. The issue I'm having now is that the factory function is envoked before the http request, causing errors in my app.

Here is the code:

App.factory('metrics', function($http){
    var service;
    var users = [{laps:[]}];
    var updateMetrics = function(){
        //updates the users array in the factory
    };
    $http.get('data.csv').success(function(data) {
        var temp_array = data.split(" ");
        updateMetrics(0, temp_array);
    });

    service.lastLapInfo = function(){
        var lastlap = [];
        for (var i=0; i<users.length;i++)
        {
            var lap = users[i].laps[users[i].laps.length-1];
            lastlap.push(lap);
        }
        return lastlap;
    };
    return service;
});

App.controller('mainController', function($scope, $http, metrics) {
    $scope.users=metrics.lastLapInfo();
});

lastLapInfo() is getting called before the http request which is causing errors as there is no data in the array. Any thoughts?

Additionally - if I'm generally going about the wrong way to meet my use case (e.g. I should be using something else instead of a Factory) let me know!

darudude
  • 541
  • 5
  • 9
  • 21

2 Answers2

2

This is a typical use case of a promise, or the angular's $q service.

The following approach, uses a factory that returns a promise:

App.factory('metrics', function($http){
   var service;
   ...
   service.load = function () {
      return $http.get('data.csv');
   });
   return service;
});       

In the controller you then call your metrics service, and you make use of the promise:

App.controller('mainController', function($scope, $http, metrics) {
    metrics.load().then(function(response) {
       // at this point you know for sure that the request has terminated
       // you can call the service again to run lastLapInfo or do the logic in here.
    });
});
bengro
  • 1,004
  • 10
  • 19
  • Very cool! Extending this how would I go about setting that http request to poll every second? Only think I can think off, is to have another function e.g update() in the factory, and call that function at a particular interval from each one of my controllers using the factory. Is there a better way? – darudude Oct 10 '14 at 22:18
  • you can use $interval service to repetitively load the load method in your service. This is further explained here: http://stackoverflow.com/questions/14944936/angularjs-global-http-polling-service?answertab=votes#tab-top with an example here http://plnkr.co/edit/iMmhXTYweN4IrRrrpvMq?p=preview. – bengro Oct 10 '14 at 22:38
1

If you don't have the data immediately, either deal with that by indicating it in the return value (null, undefined, []), or return a promise instead, which you'd also store and resolve later after fetching the data initially.

So something like

var deferred = null;

$http.get(...).then ({
  if (deferred) {
    deferred.resolve(result);
    deferred = null;
  }
});

service.lastLapInfo = function(){
  if (no metrics) {
    deferred = $q.defer()
    return deferred;
  }
  else {
    return metrics;
  }
};

should work, depending on how you want to structure it.

Edit: linked $q from comment.

ferada
  • 268
  • 7
  • 16