I have a function in my controller that calls an api to retrieve some values:
$scope.Refresh= function(){
$http.get('/get/value')
.success(function(data) {
//some actions
})
.error(function(data) {
//some actions
});
} ;
I want to refresh the values occasionally, so I've done:
setInterval($scope.Refresh, 100000);
I will do in a better way, but now I want to solve this.
but there is a problem:
If, in the controller, I say: $scope.Refresh (to execute the function first time), the controller does nothing.
If I write the same function + setInterval (to test and run it) it works first time (outside the function), but never refresh next times (code function inside), to explain, that execute the function but neither .success nor .error is called.
I have seen the headers with a 304 status (not modified) but the values are modified!!
I tried to disable cache but that did not fix the problem.
I tried to give a random value to the route like: /get/value/(randomNuber) but I get nothing
Where is the problem?
Just running:
$scope.Refresh();
should definitely run the function at least once. If it doesn't something is wrong with your code or with your server route. But you should be getting a console error if that's the case.
For setInterval, you should be using the $interval service that ensures your code is run within the angular loop.
Also, per the documentation, you should explicitly cancel this interval when your controller is destroyed.
var httpInterval = $interval($scope.Refresh, 100000);
$scope.$on('$destroy', function() {
$interval.cancel(httpInterval);
});
I've only had intermittent luck with .success and .error, and I'd like to think that part of it was caching the request. I have very consistent, successful results using .then, as shown:
$scope.Refresh= function(){
var myGet = $http.get('/get/value');
myGet.then(function(data){
//do success things here
}, function(data){
//do error things here
});
};
Other than that, follow the advice that #theJoeBiz gave regarding $interval and you should be fine.
Related
I'm having a hard time understanding how unit testing with Karma+Jasmine works exactly
I'm trying to setup some Unit Tests for my AngularJS app but I'm running into a problem where the Tests don't seem to run a function properly.
I have a function that reads a json file through a $http.get request and populates an array, but when I set up in my unit test to expect the length of the array expect(array.length).toBe(10), it always fails, yet that is the length of the array when I run the App normally. Actually if I print the length in the console.log on the unit test it seems to always be stuck at 1.
But for example if I check some of the variables I declare manually then the expect function works properly.
Can anyone tell me what exactly am I missing here? It's seems i'm missing some of the fundamentals on how unit testing with Karma/Jasmine works.
Any help would be greatly appreciated. Thanks!
Your problem is that $http.get is an asynchronous method. Unless you sepcifically tell angularjs that it needs to flush that response, it will not. The method will exit and the callback of $http.get will not happen in time for your it method to utilize its $scope transformations.
You can achieve this by expecting the call to happen with the $httpBackend API https://docs.angularjs.org/api/ngMockE2E/service/$httpBackend
You can then do
$httpBackend.expectGET('../json/apps.json').respond(200, {mock: 'api response');
$scope.getAllApps();
$httpBackend.flush();
expect(scope.allApps.length).toBe(10);
In your unit tests you should never rely on the actual response of http calls. To ensure you are testing only the method you should always mock the response of what your API is supposed to return.
ERRONEOUS
$scope.getAllApps = function(){
$scope.isLoading = true;
$http.get('../json/apps.json').then(function (data){
// ...
});
$scope.isLoading = false;
};
BETTER
$scope.getAllApps = function(){
$scope.isLoading = true;
$http.get('../json/apps.json').then(function (data){
// ...
}).finally(function() {
$scope.isLoading = false;
});
};
Because the $http service in non-blocking and asynchronous, it immediately returns a promise. It does not wait for data to return from the server. The isLoading flag needs to wait for the promise to resolve before setting the flag false.
I have an Angular SPA I'm running with Angular Loading Bar and AngularJS v1.4.9.
For some time now, it has been so happening that after the app gets loaded, the bar has been getting stuck after a while, indicating that not all requests are done with. Additionally, one of the console.log()s I have in our code have been firing continuously, around 1-2 times every second. The bar completes and the console.log works normally when the user reloads the page(but doesn't stop on its own).
The console.log() is set inside a function attached to a ng-disabled directive, so I know it's an indicator of a digest cycle in progress.
I use Chrome as my browser and I recently did a profiling run.
Here's some screenshots of what I see:
This is a broad view. As is shown here, it's first happening at 100ms, then at 400, then at 600, and so on(I did a 3s run).
This is the very first vertical strip. Not all of them look exactly the same as this one, but the completeOutstandingRequest, timeout and Browser.self.defer methods are always there. The searchDisable and log methods are ours, the log is the one I'm talking about above.
Here's another one for comparison, but this is slightly different - it has another Browser method: self.url. I'm not sure what it does.
Here are some issues I found which could be related:
Timeout callback can occur in the middle of a digest cycle in Firefox
$browser.defer ends up triggering changeDetection in zonejs through settimeout
P.S. I think this issue first started when we added some interceptors to our code to do some automatic redirects - e.g. when the session has timed out and the user clicks on something, he's automatically returned to the login page to relogin.
This is the interceptor:
interceptor.$inject = ['$rootScope', '$q'];
function interceptor($rootScope, $q) {
return {
responseError: function (rejection) {
var config = rejection.config || {};
if (!config.ignoreAuthModule) {
switch (rejection.status) {
case 401:
var deferred = $q.defer();
$rootScope.$broadcast('event:auth-loginRequired', rejection);
return deferred.promise;
case 403:
$rootScope.$broadcast('event:auth-forbidden', rejection);
break;
}
}
return $q.reject(rejection);
}
};
}
$httpProvider.interceptors.push(interceptor);
This issue has been solved.
It turns out that in case of any Unauthorized requests, the promise that is returned on line 11 above was remaining pending forever, because there was no call to .resolve() or .reject() in the deferred object.
As a result, the loading bar's interceptor was blocked.
I ended up removing the custom promise entirely and that solved the problem.
My question is very simple unlike
afterEach (function () {
$httpBackend.verifyNoOutstandingExpectation ();
$httpBackend.verifyNoOutstandingRequest ();
});
why $httpBackend.flush() cant be placed inside afterEach(function(){} ??
Because when I have several test cases, every time I need to call it.
describe("test1", function(){
it('1', function(){
$httpBackend.flush()
})
it('2', function(){
$httpBackend.flush()
})
it('3', function(){
$httpBackend.flush()
})
})
Even I tried to put $httpBackend.flush() inside after each block , but I started getting error " Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
".
Is there any other way there by I can have only one block to be automatically get executed after every function for $httpBackend.flush()
Thanks
$httpBackend.flush should be called in your tests, not after the test.
It is a part of the test so it does not make sense executing it after the test.
flush simulates sending your request to the server after which your possible httpBackend.expectXX method will either fail or pass. There's also other conditions you might want to check on.
If you want to write a helper method to avoid calling it all the time you could write something like this for example:
function executeRequestAndFlush(requestToExecute) {
requestToExecute();
$httpBackend.flush();
}
I'm new in AngularJS so if the question is not 'intelligent' for you, please don't rate it in negative. If someone ask a question, for he isn't stupid.
So..
I would like to use data from an ajax request, like this:
encryptApp.factory('getData', function($http, $rootScope) {
var getData = {};
getData.tot_of = function() {
return $http.get('/path/to').then(function(result) {
return result.data;
});
}
getData.get_info = function() {
return $http.get('/path/to').then(function(result) {
return result.data;
});
}
return getData;
});
In controller I use this:
getData.get_info().then(function(get_info) {
$scope.get_info = get_info;
});
// HERE THE $scope.get_info is UNDEFINED
I'm new in AngularJS and I don't know why does this. So, is there a method that I can use the json data outside the " then function ".
Thanks and please don't rate this question negative. Sorry if my english is not good.
$http.get returns a promise.
By essence, a promise is as Javascript saying:
"Hey ! I let you make the request, but please, I don't want to wait for you, so when you finished, please execute the callback I'm just passing you, since now, I will forget you since I have more code to execute while you're doing your job".
In other words, a promise's callback isn't executed immediately, since the goal is to not block the Javascript "thread" (Javascript is like single-threaded).
So your current code is acting like this:
getData.get_info().then(function(get_info) { //the function inside this "then" IS the callback
$scope.get_info = get_info;
});
// Hey !! The request might not finish ! So don't expect $scope to have the value you expect here !
So the simple example to illustrate would be to imagine that your ajax request takes 100ms to execute.
Within those 100ms, your next Javascript scope is very very very likely to be already reached, having $scope.get_info not initialized yet.
Without promise, your next code, outside of the callback, that should not depend of $scope.get_info, would have to wait 100ms to start, wasting time.
So, is there a method that I can use the json data outside the " then
function ".
There is a way, using broadcasting/emit ($rootScope.$broadcast/$rootScope.$emit) to trigger a corresponding event, but it's often more "anti-KISS" for a simple case.
I advise you to put all your depending code in the promise callback.
To clean your code, merely call a private function that you define outside the callback.
I could not find anything in the docs about this, but it seems that any request has to be inside a $apply() call - (wether this $apply() call comes from an action or is invoked manually).
I can not explain this strange behavior any other way:
// inside a controller
$scope.resources = Resource.query();
// a request gets sent
works just fine, but
// somewhere else - in a callback for auto-complete
// just to show that this is outside $scope.$apply() - not realy setTimeout
setTimeout(function(){
$scope.resources = Resource.query();
},100);
// no request gets sent;
});
I think you are looking at this issue: https://github.com/angular/angular.js/issues/2371.
You may want to follow up.