angularjs http service wait for data - angularjs

I have HTTP service that returns promise to inspection2update.DamageTypeId property and continue to execute further.
HTTP service:
function post(objectTypeId, damageDescription) {
var defer = $q.defer();
$http.post(serviceAddress + "save/" + objectTypeId + "/" + damageDescription).then(function (response) {
defer.resolve(response.data);
});
return defer.promise;
}
Here how I call service in controller:
inspection2update.DamageTypeId = damageTypesService.save(inspection2update.ObjectTypeId, self.dType);
But I need to wait until I get data from service and only after it, to execute further.
For this purpose I use $q service inside $http resolver, but still I get promise from my service and no data.
What do I have to change in my code to make service wait for data?

You are returning a promise which is resolved after the http call has finished fetching the data. The consumer who use this service needs to wait for the promise to resolve and then do something with the data.
Use the then syntax to receive the promise data and execute further:
damageTypesService.save(inspection2update.ObjectTypeId, self.dType).then(function(data) {
inspection2update.DamageTypeId = data;
// rest of execution...
});
P.S - as far as I can see there is no use of $q in your case (unless you want to mingle with the data / make logs, etc...). You can return the $http call as is:
function save(objectTypeId, damageDescription) {
return $http.post(serviceAddress + "save/" + objectTypeId + "/" + damageDescription);
}

Use the best of angular promise with $q
function save (objectTypeId, damageDescription) {
var deferred = $q.defer();
$http({
url: serviceAddress + "save/" + objectTypeId + "/" ,
method: 'POST',
data: damageDescription
})
.success(function (data) {
deferred.resolve(data);
})
.error(function (data) {
deferred.resolve(data);
});
return deferred.promise;
}
And in your controller use .then function
damageTypesService.save(inspection2update.ObjectTypeId, self.dType).then(function(response){
/*use the response here
eg: inspection2update.DamageTypeId = response.id
*/
})

First off, you have your service method named post and your are calling a method called save. Is that an mistake? Second, I don't see any reason you should be using the $http service it is low level and your request is simple. You should checkout $resource it provides a higher level of abstraction and will work for straightforward requests like yours. Now, onto your problem. both $http and $resource always return a promise. so, typically in your service or controller you provide a callback that takes the response received from the request and processes it. Since the approach for $resource and $http are similar, but you asked about $http I will show you using $http.
function post(objectTypeId, damageDescription) {
return $http.post(serviceAddress + "save/" + objectTypeId + "/" + damageDescription);
}
Now, in your controller you call the service method post() like this.
damageTypesService.post(inspection2update.ObjectTypeId, self.dType).then(mycallback);
function myCallback(response){
inspection2update.DamageTypeId = response; // DamageTypeId now won't be populated until the request is resolved.
}

Related

Ensuring all Api's has been loaded in a page in AngularJS

I have 4 controllers in a page in AngularJS. Each controller calls Api's via http request(scope $http). I want to ensure that all the Api's has been called and loaded till then I can show the loading gif image. How to check all the Api's has been loaded in the page in AngulaJS.
I am not sharing the exact code some variable and name I have modified.
myApp.controller('testController',function ($scope, $http, $q, $filter) {
var _promises = {};
_promises['abc'] =
$http({
url: API_URL+'abc-type/',
method: 'GET',
params: {'test1': 'test2'}
});
_promises['abc1'] =
$http({
url: API_URL+'abc-type2/',
method: 'GET'
});
}
$q.all(_promises).then(function (res) {
alert("All promises executed.");
});
});
$http uses promises ($q) for it's API. You can use the $q.all method to run a callback when an array of $http requests are resolved (you will need to make sure all service requests return promises to avoid undefined behavior).
Combines multiple promises into a single promise that is resolved when all of the input promises are resolved.
It would look something like this
$scope.showLoadingGif = true;
$q.all([MyService.makeGet(), MyService.makeAnotherGet(), ...]).then(function(responses) {
// all the calls have returned a response by this point
$scope.showLoadingGif = false;
})

Mock $http with configuration parameters

I'm applying some tests in an existing AngularJS application in order to ensure it's correct behaviour for future changes in the code.
I am pretty new with Jasmine & Karma testing, so I've decided to start with a small and basic service which performs an http request to the backend, and waits for the result with a promise, nothing new.
Here's the service method to test:
function getInformedConsent(queryParameters) {
var def = $q.defer(),
httpParameters = {
url: ENV.apiEndpoint + '/urlResource',
method: 'GET',
params: queryParameters,
paramSerializer: '$httpParamSerializerJQLike'
};
$http(httpParameters)
.then(
function (response) {
def.resolve(response);
},
function (error) {
def.reject(error);
}
);
return def.promise;
}
And here my test:
it('getInformedConsent method test', function() {
$httpBackend.expectGET(/.*\/urlResource?.*/g)
.respond(informedConsentJson.response);
var promise;
promise = InformedconsentService.getInformedConsent(informedConsentJson.queryParameters[0]);
promise
.then(function(response) {
console.log(response);
expect(response).toEqual(informedConsentJson.response);
});
$httpBackend.flush();
});
informedConsentJson as you can supose, is a fixture with input and the expected output.
Reading AngularJS documentation, I decided to use $httpBackend, because it's already a mock of $http service, so I thought it could be useful.
The problem is that somewhere in the code, someone is broadcasting a "$locationChangeStart" event and executing
$rootScope.$on('$locationChangeStart', function (event,current,old) {
/* some code here */
});
in app.js.
I'm not trying to change the URL, i'm just trying to get some data from the mocked backend.
I asume that is because I'm not using $http mock ($httpBackend) as it should be used.
Anyone can help me with $http with configuration JSON mock?
It's freaking me out.
Thank you all in advance for your time and your responses

How to order same ajax calls in a loop?

I am getting some information from back end using the following code:
angular.forEach(authors, function(author){
var authorId = author.id;
var url = contextPath + '/book/list/' + authorId;
$http({
method: 'GET',
url: url
})
.then(function(response){
author.books = response.data;
})
});
I want to trigger the ajax calls one by one not all at the same time, which means each ajax call would wait for the previous one to be completed. Does anybody know what would be the change in my code?
Yes you can make call on a sequence basis by using $timeout service in angular as below
angular.forEach(authors, function(author){
var authorId = author.id;
var url = contextPath + '/book/list/' + authorId;
$timeout(function(){
$http({
method: 'GET',
url: url
})
.then(function(response){
author.books = response.data;
})
})},2000);//2000-> milliseconds;
This makes your GET request on every 2 seconds. If you know that your call will take some more time set the timeout value as required.
Take a look at $q.all()
Combines multiple promises into a single promise that is resolved when all of the input promises are resolved.
EDIT:
Ooops , didn't quite read it. $q.all() wait for all promises to resolve, you need to chain them so after one promise is resolved you call another:
somepromise
.then(function(response){
return $http({method:"some method",url:"some urlj",data:response});
}).then(function(response2){
//chain third and so on
})
basically you need to return promise to be able to chain them

How to use $http promise response outside success handler

$scope.tempObject = {};
$http({
method: 'GET',
url: '/myRestUrl'
}).then(function successCallback(response) {
$scope.tempObject = response
console.log("Temp Object in successCallback ", $scope.tempObject);
}, function errorCallback(response) {
});
console.log("Temp Object outside $http ", $scope.tempObject);
I am getting response in successCallback but
not getting $scope.tempObject outside $http. its showing undefined.
How to access response or $scope.tempObject after $http
But if I want to use $scope.tempObject after callback so how can I use it. ?
You need to chain from the httpPromise. Save the httpPromise and return the value to the onFullfilled handler function.
//save httpPromise for chaining
var httpPromise = $http({
method: 'GET',
url: '/myRestUrl'
}).then(function onFulfilledHandler(response) {
$scope.tempObject = response
console.log("Temp Object in successCallback ", $scope.tempObject);
//return object for chaining
return $scope.tempObject;
});
Then outside you chain from the httpPromise.
httpPromise.then (function (tempObject) {
console.log("Temp Object outside $http ", tempObject);
});
For more information, see AngularJS $q Service API Reference -- chaining promises.
It is possible to create chains of any length and since a promise can be resolved with another promise (which will defer its resolution further), it is possible to pause/defer resolution of the promises at any point in the chain. This makes it possible to implement powerful APIs.1
Explaination of Promise-Based Asynchronous Operations
console.log("Part1");
console.log("Part2");
var promise = $http.get(url);
promise.then(function successHandler(response){
console.log("Part3");
});
console.log("Part4");
The console log for "Part4" doesn't have to wait for the data to come back from the server. It executes immediately after the XHR starts. The console log for "Part3" is inside a success handler function that is held by the $q service and invoked after data has arrived from the server and the XHR completes.
Demo
console.log("Part 1");
console.log("Part 2");
var promise = new Promise(r=>r());
promise.then(function() {
console.log("Part 3");
});
console.log("Part *4*");
Additional Resources
Angular execution order with $q
What is the explicit promise construction antipattern and how do I avoid it?
Why are angular $http success/error methods deprecated? Removed from v1.6?
How is javascript asynchronous AND single threaded?
Ninja Squad -- Traps, anti-patterns and tips about AngularJS promisesGood theory but needs to be updated to use .then and .catch methods.
You're Missing the Point of Promises
$http call is async call. The callback function executes when it has returned a response. Meanwhile the rest of the function keeps executing and logs $scope.tempObject as {}.
When the $http is resolved then only $scope.tempObject is set.
Angular will bind the changed value automatically using two way binding.
{{tempObject}} in the view will update itself.
if you want to use tempObject after callback then do this
then(function(data){
onSuccess(data);
},function(){
});
function onSuccess(data){
// do something
}
Try to use a $scope.$apply before to finish the successCallback function. An other solution is to change successCallback -> function so:
$http({ method: 'GET', url: '/myRestUrl' }).then(function(success) { $scope.tempObject = success; console.log("Temp Object in successCallback ", $scope.tempObject); }, function(error) { });

Property on $scope set inside of $http.success() is undefined outside of $http.success(). Why?

I am following an AngularJS tutorial that uses $resource to retrieve JSON data from an API call. For the purpose of understanding, I tried to replace the $resource code with $http code and I encountered a scope problem. Logging $scope.weatherResult outside of .success() results in undefined. Why is that the case? The view receives the data just fine.
Also,
// $scope.weatherAPI = $resource(
'http://api.openweathermap.org/data/2.5/forecast/daily',
{ callback: 'JSON_CALLBACK' }, { get: { method: 'JSONP' }}
);
// $scope.weatherResult = $scope.weatherAPI.get({ q: $scope.city, cnt: 2});
$http.get('
http://api.openweathermap.org/data/2.5/forecast/daily'
+ '?q='
+ $scope.city
+ '&'
+ 'cnt=2'
)
.success(function(data) {
$scope.weatherResult = data;
})
.error(function(error) {
console.log(error);
});
console.log($scope.weatherResult);
Because $http is asynchronous.
$scope.weatherResult is defined only when the http response is available.
See for example http://code.tutsplus.com/tutorials/event-based-programming-what-async-has-over-sync--net-30027, or better, as PSL says: How do I return the response from an asynchronous call?
You can use $watch to be informed:
$watch('weatherResult',function(newValue,oldValue)) {
..
}
When you write
.success(function(data) {
$scope.weatherResult = data;
})
in your program, you are asking the remaining part of your code to continue its execution with a promise.
In this case console.log($scope.weatherResult);
will be executed just after your $http.get() method without waiting for the response from the http request.
Hence, console.log($scope.weatherResult); will be executed even before the API response is received.
Note that $scope.weatherResult is defined inside .success(), so until the response is a success, Angular has no idea about $scope.weatherResult hence the console gives undefined. It will be undefined even in case of an error.
To view the response of server, you can log it well inside success block.
.success(function(data) {
$scope.weatherResult = data;
console.log("$scope.weatherResult = ",$scope.weatherResult);
})

Resources