Próbuję się nauczyć AngularJS. Moja pierwsza próba uzyskania nowych danych co sekundę zadziałała:
'use strict';
function dataCtrl($scope, $http, $timeout) {
$scope.data = [];
(function tick() {
$http.get('api/changingData').success(function (data) {
$scope.data = data;
$timeout(tick, 1000);
});
})();
};
Kiedy symuluję powolny serwer, śpiąc wątek przez 5 sekund, czeka na odpowiedź przed aktualizacją interfejsu użytkownika i ustawieniem kolejnego limitu czasu. Problem pojawia się, gdy przepisałem powyższe, aby użyć modułów Angular i DI do tworzenia modułów:
'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);
})();
};
Działa to tylko wtedy, gdy odpowiedź serwera jest szybka. Jeśli występuje jakieś opóźnienie, wysyła 1 żądanie na sekundę bez czekania na odpowiedź i wydaje się czyścić interfejs użytkownika. Myślę, że muszę użyć funkcji oddzwaniania. Próbowałem:
var x = Data.get({}, function () { });
ale pojawił się błąd: „Błąd: miejsce docelowe.push nie jest funkcją” Zostało to oparte na dokumentacji dla $ zasób, ale nie rozumiałem tam przykładów.
Jak sprawić, by drugie podejście zadziałało?
źródło
Nowsze wersje angulara wprowadziły interwał $ interwał, który działa nawet lepiej niż $ timeout przy odpytywaniu serwera.
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; } });
źródło
Oto moja wersja z odpytywaniem rekurencyjnym. Oznacza to, że będzie czekał na odpowiedź serwera przed zainicjowaniem następnego limitu czasu. Ponadto, gdy wystąpi błąd, odpytywanie będzie kontynuowane, ale w bardziej swobodnej rezydencji i zgodnie z czasem trwania błędu.
Demo jest tutaj
Więcej o tym napisałem tutaj
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...'; });
źródło
Możemy to łatwo zrobić odpytywaniem za pomocą usługi $ interwał. tutaj jest szczegółowy dokument na temat $ interwału
https://docs.angularjs.org/api/ng/service/$interval
Problem z używaniem $ interwału polega na tym, że jeśli wykonujesz wywołanie usługi $ http lub interakcję z serwerem i jeśli opóźnienie przekracza $ interwał następnie zanim jedno żądanie zostanie zakończone, rozpocznie kolejne żądanie.
Rozwiązanie:
1. Sondowanie powinno być prostym pobieraniem statusu z serwera, takim jak pojedynczy bit lub lekki plik JSON, więc nie powinno trwać dłużej niż zdefiniowany interwał. Aby uniknąć tego problemu, należy również odpowiednio zdefiniować przedział czasu.
2. Jakoś to się nadal dzieje z jakiegokolwiek powodu, powinieneś sprawdzić globalną flagę, że poprzednie żądanie zakończyło się, czy nie, przed wysłaniem jakichkolwiek innych żądań. Pominie ten przedział czasu, ale nie wyśle żądania przedwcześnie.
Również jeśli chcesz ustawić wartość progową, która po jakiejś wartości powinna być ustawiona tak czy inaczej, możesz to zrobić w następujący sposób.
Oto działający przykład. szczegółowo wyjaśniono tutaj
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; } }]);
źródło