Estoy tratando de aprender AngularJS. Mi primer intento de obtener nuevos datos cada segundo funcionó:
'use strict';
function dataCtrl($scope, $http, $timeout) {
$scope.data = [];
(function tick() {
$http.get('api/changingData').success(function (data) {
$scope.data = data;
$timeout(tick, 1000);
});
})();
};
Cuando simulo un servidor lento durmiendo el hilo durante 5 segundos, espera la respuesta antes de actualizar la interfaz de usuario y establecer otro tiempo de espera. El problema es cuando reescribí lo anterior para usar módulos angulares y DI para la creación de módulos:
'use strict';
angular.module('datacat', ['dataServices']);
angular.module('dataServices', ['ngResource']).
factory('Data', function ($resource) {
return $resource('api/changingData', {}, {
query: { method: 'GET', params: {}, isArray: true }
});
});
function dataCtrl($scope, $timeout, Data) {
$scope.data = [];
(function tick() {
$scope.data = Data.query();
$timeout(tick, 1000);
})();
};
Esto solo funciona si la respuesta del servidor es rápida. Si hay algún retraso, envía una solicitud por segundo sin esperar una respuesta y parece borrar la interfaz de usuario. Creo que necesito usar una función de devolución de llamada. Lo intenté:
var x = Data.get({}, function () { });
pero obtuve un error: "Error: destination.push no es una función" Esto se basó en los documentos para $ resource pero realmente no entendí los ejemplos allí.
¿Cómo hago que funcione el segundo enfoque?
fuente
Las versiones más recientes de angular han introducido $ interval, que funciona incluso mejor que $ timeout para el sondeo del servidor.
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; } });
fuente
Aquí está mi versión con sondeo recursivo. Lo que significa que esperará la respuesta del servidor antes de iniciar el próximo tiempo de espera. Además, cuando ocurra un error, continuará la encuesta, pero de una manera más relajada y de acuerdo con la duración del error.
La demostración está aquí
Escrito más sobre esto aquí
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...'; });
fuente
Podemos hacerlo mediante el sondeo fácilmente usando el servicio $ interval. aquí hay un documento detallado sobre $ interval
https://docs.angularjs.org/api/ng/service/$interval El
problema al usar $ interval es que si está haciendo una llamada de servicio $ http o interacción con el servidor y si se demora más de $ intervalo de tiempo luego, antes de que se complete su solicitud, inicia otra solicitud.
Solución: 2. De alguna manera, todavía está sucediendo por cualquier motivo, debe marcar un indicador global de que la solicitud anterior terminó o no antes de enviar cualquier otra solicitud. Perderá ese intervalo de tiempo, pero no enviará la solicitud antes de tiempo. Además, si desea establecer un valor de umbral que después de algún valor de todos modos se debe establecer el sondeo, puede hacerlo de la siguiente manera. Aquí está un ejemplo de trabajo. explicado en detalle
1. El sondeo debe ser un estado simple que se obtiene del servidor como un solo bit o json liviano, por lo que no debe tomar más tiempo que el intervalo definido. También debe definir el tiempo del intervalo de manera adecuada para evitar este problema.
aquí
angular.module('myApp.view2', ['ngRoute']) .controller('View2Ctrl', ['$scope', '$timeout', '$interval', '$http', function ($scope, $timeout, $interval, $http) { $scope.title = "Test Title"; $scope.data = []; var hasvaluereturnd = true; // Flag to check var thresholdvalue = 20; // interval threshold value function poll(interval, callback) { return $interval(function () { if (hasvaluereturnd) { //check flag before start new call callback(hasvaluereturnd); } thresholdvalue = thresholdvalue - 1; //Decrease threshold value if (thresholdvalue == 0) { $scope.stopPoll(); // Stop $interval if it reaches to threshold } }, interval) } var pollpromise = poll(1000, function () { hasvaluereturnd = false; //$timeout(function () { // You can test scenario where server takes more time then interval $http.get('http://httpbin.org/get?timeoutKey=timeoutValue').then( function (data) { hasvaluereturnd = true; // set Flag to true to start new call $scope.data = data; }, function (e) { hasvaluereturnd = true; // set Flag to true to start new call //You can set false also as per your requirement in case of error } ); //}, 2000); }); // stop interval. $scope.stopPoll = function () { $interval.cancel(pollpromise); thresholdvalue = 0; //reset all flags. hasvaluereturnd = true; } }]);
fuente