Server polling with AngularJS
Solution 1:
You should be calling the tick
function in the callback for query
.
function dataCtrl($scope, $timeout, Data) {
$scope.data = [];
(function tick() {
$scope.data = Data.query(function(){
$timeout(tick, 1000);
});
})();
};
Solution 2:
More recent versions of angular have introduced $interval which works even better than $timeout for server polling.
var refreshData = function() {
// Assign to scope within callback to avoid data flickering on screen
Data.query({ someField: $scope.fieldValue }, function(dataElements){
$scope.data = dataElements;
});
};
var promise = $interval(refreshData, 1000);
// Cancel interval on page changes
$scope.$on('$destroy', function(){
if (angular.isDefined(promise)) {
$interval.cancel(promise);
promise = undefined;
}
});
Solution 3:
Here is my version using recursive polling. Which means it'll wait for the server response before initiating the next timeout. Also, when an error occur it'll continue polling but in a more relaxed manor and according to the duration of the error.
Demo is here
Written more about it in here
var app = angular.module('plunker', ['ngAnimate']);
app.controller('MainCtrl', function($scope, $http, $timeout) {
var loadTime = 1000, //Load the data every second
errorCount = 0, //Counter for the server errors
loadPromise; //Pointer to the promise created by the Angular $timout service
var getData = function() {
$http.get('http://httpbin.org/delay/1?now=' + Date.now())
.then(function(res) {
$scope.data = res.data.args;
errorCount = 0;
nextLoad();
})
.catch(function(res) {
$scope.data = 'Server error';
nextLoad(++errorCount * 2 * loadTime);
});
};
var cancelNextLoad = function() {
$timeout.cancel(loadPromise);
};
var nextLoad = function(mill) {
mill = mill || loadTime;
//Always make sure the last timeout is cleared before starting a new one
cancelNextLoad();
$timeout(getData, mill);
};
//Start polling the data from the server
getData();
//Always clear the timeout when the view is destroyed, otherwise it will keep polling
$scope.$on('$destroy', function() {
cancelNextLoad();
});
$scope.data = 'Loading...';
});