Angular.js code with $httpProvider and promise. What does it do? - angularjs

Could you explain in general what does this code do:
App.config(['$httpProvider', function ($httpProvider) {
$httpProvider.responseInterceptors.push('HttpSpinnerInterceptor');
$httpProvider.defaults.transformRequest.push(function (data, headersGetter) {
angular.element('.brand img').attr("src","<%= asset_path('brand/brand.gif') %>");
return data;
});
}]);
App.factory('HttpSpinnerInterceptor', function ($q, $window) {
return function (promise) {
return promise.then(function (response) {
angular.element('.brand img').attr("src","<%= asset_path('brand/brand.png') %>");
return response;
}, function (response) {
angular.element('.brand img').attr("src","<%= asset_path('brand/brand.png') %>");
return $q.reject(response);
});
};
});
I have completely no understanding except some guesses that it intercepts some response and injects a src attribute of image.
I do not understand how and when is HttpSpinnerInterceptor called and what the "promise" parameter is.

HttpSpinnerInterceptor is been called after each request issued by using $http service is completed (successfully or not), but before promise is been resolved to caller (so you can defer result). Actually transform request is not needed, because it does mostly same as HttpSpinnerInterceptor (or HttpSpinnerInterceptor is not needed...), because it does not transform anything.
promise parameter is a $q promise that could be used in case if you need to perform some async actions when with result of your request as you can resole it later, so caller would get result later. Actually in your code, you directly resolve this promise (or reject it), changing src attribute of the image.
Here are some links to documentation:
Using $http service: http://docs.angularjs.org/api/ng.$http - take careful look at "Response interceptors" and "Transforming Requests and Responses"
Promises in AngularJS: http://docs.angularjs.org/api/ng.$q

Related

What does $q.defer() in following code in angularjs context

What is the use of $q.defer() in following code. I am bit confused or not able to understand the use of $q or defer?
service.serviceCall = function (methodName, params) {
var deferred = $q.defer();
$http({ method: "POST", url: url + methodName, data: params, headers: headers }).success(function (result) {
deferred.resolve(result);
}).error(function (result) {
deferred.reject(result);
});
return deferred.promise;
}
service.serviceCal("POST", {"param1":"value1"}).then(function(data){
//here data will be object which is resolved in success call
}).fail(function(){
//here data will be object which is rejected in failure call
})
This are promises, we user $q.defer() to return a promise using defer.promise(). It is contract between the calling object and promise that in future the calling object (here then and fail) will ultimately get a value either in then or fail depends whether it is resolved or rejected.
Read about promises here: Promises
Go through docs of $q module: $q Module Angular
A service that helps you run functions asynchronously, and use their
return values (or exceptions) when they are done processing.
So promises are used in async programming. $q is Angular's implementation of promises.
Usage of the above function:
service.serviceCal(methodName, params)
.then(function(resolve){
// on successful resolving
// called when defer.resolve is called
// resolved object: resolve
}, function(reject){
// on reject
// called when defer.reject is called
// rejected object: reject
})
The code is using deprecated methods on the result of calling $http. Although $http returns a promise it has some additional methods .success and .error that don't quite work within the usual promise structure.
$q.defer() creates a promise which is returned from the function and this code uses success and error to resolve the promise. A simpler way to write this code would be to just use the promises returned by $http and its .then method.
service.serviceCall = function (methodName, params) {
return $http({ method: "POST", url: url + methodName, data: params, headers: headers })
.then(function successFn (response) {
return response.data;
});
}
This has the same effect as the original code: it returns a promise which either resolves to the data from the response, or if an error occurs the promise is rejected.

Does Angular1.4 $http include a promise already in the http get? Or do I make my own

I'm a bit confused, the more I read resources online about $q and $http the more my head spins. So if I do a $http.get call does that not include a promise? Or do I bring $q in?
It is built on $q and returns a promise. See the docs: https://docs.angularjs.org/api/ng/service/$http
And the example there:
$http({
method: 'GET',
url: '/someUrl'
}).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
$http.get is just a convenience method on the above.
You can refer to the following link
https://www.peterbe.com/plog/promises-with-$http
This service ($http.get()) will return promise as success callback and error callback...so this function itself returns promise. You just have to handle it
The $http API is based on the deferred/promise APIs exposed by the $q
service. While for simple usage patterns this doesn't matter much, for
advanced usage it is important to familiarize yourself with these APIs
and the guarantees they provide.
https://docs.angularjs.org/api/ng/service/$http
Meaning that the $http.get will return a promise anyway. No need nesting your own $q approach. Just return the $http invoke.
$http.get call does that not include a promise? The Answer is Yes, you can return a promise or resolve the promise with $http
Returning a promise from $http.get
getData: function() {
return $http.get('some url'); // you can resolve this promise later
// using (then)
}
So later in your code you can resolve the above promise like this
...
myService.getData().then(function(response) {
// do something with response
}).catch()
Resolve the promise inline
getData: function() {
$http.get('some url').then(function(response) {
// do something with response
}).catch()
}

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) { });

Fail-Proof UI-Router Resolve w/ Restangular?

I am attempting to implement UI-router resolves which will return a result even if the API call fails. Our app has back-end permissions, and if the call fails, I still need to show nested pages (which won't load if a resolve calls fails normally, unless it is wrapped into a $q promise. I implement it like this with $http:
resolve: {
kittens: ["$q", "$timeout","$http", function ($q, $timeout, $http,) {
var url = "some api url"
return $http.get (url)
.then(function(result){ return {status:true , data: result} },
function(){ return {status:false} }); //on failure, return this
}],
}
The above works perfectly - it returns what I need on both call success and failure, however, it seems to fail if I try it with Restangular, the code below works fine:
kittens: function (Restangular) {
return Restangular
.one('stuff', 999999999999)
.all('stuffInStuff').getList()
.then(function (result) {
return result;
});
},
but if I try this with the above:
.then(function (result) {
return result;
}, function(error){return error})
the failure doesn't return anything and the controller isn't instantiated. I don't understand why this happens. I thought both $http.get() and Restangular.one().all().getList() (as an example) are both equivalent promises, that either return a result or fail. What's the difference? How do I provide a resolve value on call fail with Restangular?
Edit: Btw, I did read this post, and I understand that if a UI-router resolve isn't wrapped, it fails if the promise is rejected, but I don't seem to fully get how to approach it with Restanagular...
edit 3: This fails as well:
.then(function (result) {
return result;
})
.catch(function (error) {
return error;
})
I think you have two ways ,
1.) Dont return restangular instance inside resolve of ui router, because you are returning promise which is rejected. you cant bypass this rejection natively. When you return Restangular.one().get() and it fails, your resolve fails. if you dont return instance, resolve will get resolved even when your request is still loading(which i think you dont want and you want wait for success/error)
second , for me correct way is this.
resolve: {
kittens: ["$q", "Restangular", function ($q, Restangular) {
var deferred = $q.defer();
Restangular.one('some-api').get().then(function(data) {
deferred.resolve(data);
}, function(err) {
deferred.resolve(err);
});
return deferred.promise;
}],
}
in this case, resolve will be resolved everytime and you will get transition even when rest call will fail

Delay an angular.js $http service

I have some angular factories for making ajax calls towards legacy ASP.NET .asmx web services like so:
module.factory('productService', ["$http",
function ($http) {
return {
getSpecialProducts: function (data) {
return $http.post('/ajax/Products.asmx/GetSpecialProducs', data);
}
}
} ]);
I'm testing on a local network so response times are "too" good. Is there a smart way of delaying the $http a couple of seconds from making the call to simulate a bad connection?
Or do I need to wrap all calls to the factory methods in a $timeout ?
$timeout(function() {
productService.getSpecialProducs(data).success(success).error(error);
}, $scope.MOCK_ajaxDelay);
Interesting question!
As you mentioned yourself, $timeout is the most logical choice for a delayed call. Instead of having $timeout calls everywhere, you could push a response interceptor that wraps the $http promise in a $timeout promise, as conceptually outlined in the documentation of $http, and register it in one of your configuration blocks. This means all $http calls are affected by the $timeout delay. Something along the lines of:
$httpProvider.interceptors.push(function($timeout) {
return {
"response": function (response) {
return $timeout(function() {
return response;
}, 2500);
}
};
});
As a bonus to your "to simulate a bad connection?", you could reject or do absolutely nothing randomly, too. Heh heh heh.
The new chrome device emulator has a network throttling function:
To get there: In Google Chrome, press F12 to open the Developer Tools. Then, on the top left corner, click the "Toggle device mode" icon (left to the "Elements" menu).
Developing more on the answer of #stevuu
responseInterceptors seems to be depreceted (as of 1.2.20) I have modified the code to work on the interceptors mechanism:
$httpProvider.interceptors.push(function($q, $timeout) {
return {
'response': function(response) {
var defer = $q.defer();
$timeout(function() {
defer.resolve(response);
}, 2300);
return defer.promise;
}
};
});
You could use the $q service for defer().promise pattern:
function someFunction(MOCK_ajaxDelay) {
var deferred = $q.defer();
$http.post('/ajax/Products.asmx/GetSpecialProducs', data).success(function(response) {
$timeout(function() {deferred.resolve({ success: true, response: response })}, MOCK_ajaxDelay);
}).error(function() {
$timeout(function() {deferred.resolve({ success: true, response: response } }, MOCK_ajaxDelay);
});
return deferred.promise;
}
someService.someFunction(500).then(function(data) {
if (data.success) {
$scope.items = data.response.d;
}
});
But if you are really mock testing, the better solution is to look into ngMock: http://docs.angularjs.org/api/ngMock.$httpBackend
While #stevuu's answer is correct, the syntax has changed in the newer AngularJS versions since then. The updated syntax is:
$httpProvider.interceptors.push(["$q", "$timeout", function ($q, $timeout) {
function slower(response) {
var deferred = $q.defer();
$timeout(function() {
deferred.resolve(response);
}, 2000);
return deferred.promise;
}
return {
'response': slower
};
}]);
You can achieve this using the promise api combined with a $timeout. The $http.post function returns a promise from which you can call .success and .error (these are http specific methods). This promise is resolved when the http request is complete. If you build your own promise then you can tell it to delay 2 seconds and then resolve when the http request is complete:
module.factory('productService', function ($http, $q, $timeout) {
return {
getSpecialProducts: function (data) {
var defer = $q.defer();
$http.post('/ajax/Products.asmx/GetSpecialProducs', data).success(
function(data) {
// successful http request, resolve after two seconds
$timeout(function() {
defer.resolve(data);
}, 2000)
}).error(function() {
defer.reject("Http Error");
})
return defer.promise;
}
}
});
But note - you will have to use promise.then(successCallback, errorCallback) functionality - that is, you'll lose the ability to access http headers, status & config from your controllers/directives unless you explicitly supply them to the object passed to defer.resolve({})
Links:
Defer/Promise Api
Http/Promise Api
Resolve egghead video
In response to the testing aspect of your question, Fiddler has a really useful function that helps when you need to simulate delays:
Click on the AutoResponders tab in Fiddler.
Add a rule with a regex that matches the URL of the request you want to delay.
Set the "respond with" to "*delay:1000" where the number is the delay in milliseconds.
The AutoResponder functionality in Fiddler is extremely useful for testing JS that involves a lot of http requests. You can set it to respond with particular http error codes, block responses, etc.
If you are using a service that returns a promise, then inside you should put a return before the $timeout as well because that returns just another promise.
return dataService.loadSavedItem({
save_id: item.save_id,
context: item.context
}).then(function (data) {
// timeout returns a promise
return $timeout(function () {
return data;
},2000);
});
Hope it helps someone!

Resources