Creating HTTP Response object - angularjs

While calling a service, it calls API using $http method. In the same service I want to add conditional statement which is responsible to return another success or failure object without calling any API/URL.
Something like this:
call someMethod()
{
if (statement true) { return httpResponseObject; }
else {
return responseObject; }
}
The else part I need to return object responseObject in such a way so it can invoke the error block of method which is calling someMethod() as a service.
I tried return false;, return -1;, its not working. Also, I tried using ResponseText of XMLHttpRequest but didn't work.
Any help, how to create a HTTPResponse object which can be used in same way just as it would be returned by $Http response object. Something like attached snapshot:

Why not use a simple promise and call two functions success, error callback something like
someMethod().then(function(){
//success logic
}, function(error){
//error logic
})
You can fake a promise just like mentioned in comments and then use the above logic.

As suggested by #bigless I solved by using Promise
return (Promise.reject("Error"));
It returns same object as it would have been returned by $http call.

Related

Equivalent of angular's $q.deferred.reject without $q

I imagine this has to be a pretty normal setup:
return $http.get('some/url')
.then(function (result) {
if (result.data.success) {
//Do something useful
} else {
//We've hit some sort of error
}
},
function () {
return "Failed to communicate with the server, or the server encountered an error.";
});
My questions is around what to do about scenarios where the http call technically succeeds, but the data we get back from the server is bad, unusable, or explicitly indicates something is wrong. What I want to do there is push myself onto the failure track of the promise chain. That way anyone consuming this promise well execute their own failure function, if that makes sense.
I know $q gives you the tools you need to do this by creating a deferred object and then letting you call resolve or reject under any circumstance you want, but I wondered if there was a way to do that without using $q.
To change a promise from the resolved to the rejected state, you can do one of two things from a .then() handler:
Return a rejected promise and the reason for that rejected promise will become the reject reason for the parent promise.
Throw an exception and the exception value will become the reject reason for the parent promise. .then() automatically catches exceptions in .then() handler functions and turns them into rejections.
So, here's one way you could do it:
return $http.get('some/url').then(function (result) {
if (result.data.success) {
//Do something useful
} else {
//We've hit some sort of error, make promise become rejected
throw new Error("invalid data received");
}
}).catch(function (err) {
// log error
console.log(err);
// make sure promise stays rejected
throw err;
});
If you use ES6 you can use JavaScript native promises.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

How to share a model between different controllers through a service in Angular?

I am very new in Angular and I would like to share my problem, just in case someone could help me or guide me to
figure it out the best solution.
How do I share retrieved data from a promise (it can be called only once) between different controller through a service or a factory?
I think I should avoid if it is possible emit or broadcasting more on, either timeouts or similar
Anyway, I am sure it has to exist a pattern to do this properly
service('Service', function(...) {
function ----------(....) {
return myasyncfunction('..............')
.then(function(data) {
return data;
});
}
function getSomething {
return some operation with DATA;
}
return {
getSomething: getSomething
}
}
.controller('CtrlA', function (...) {
Service.getSomething ---> Doesn't work if don't put a delay
}
.controller('CtrlB', function (...) {
}
My code always has dependencies regarding to the fact that I need a promises at the beginning to retrieve the data. I don't
know the proper way to create an architecture in which I don't need a watch, timeout or emit/broadcast & on.
I suspect it is possible to do in another way
The promise should be called only once
Thx in advance
I should also have to use .then on promise(inside your controller) returned by service like -:
Service.getSomething.then(function(result){
//your promise resolve code
},function(error){
//your promise reject code
})

Angular $http.reject vs. $q.reject

I am remember researching this awhile back and coming up empty handed and I still can't find any good info on this - why does there appear to be a $q.reject method but not an $http.reject method?
For example, in real life we might have:
unfollow: function (userId) {
if (!AuthService.isLoggedIn()) {
//$location.url('/login');
window.location.href = '/login';
return $q.reject({error: 'no logged-in user, but non-existent user could still click a follow button?'});
}
else {
return $http({
method: 'PUT',
url: ConfigService.api.baseUrl + '/v1/users/add_unfollow/by_id/' + userId
});
}
}
I would rather uses the relevant $http.reject instead of $q.reject, but that doesn't seem to work.
Since $http returns a promise in one leg of the conditional...the function itself needs to return a promise in the other leg also.
Using $q.reject() is simply a shortcut to return a rejected promise.
Without it, any place that calls unfollow().then() wouldn't have a then() method if a promise wasn't returned
$http would wrap the http calls and return a promise. The promise would rejected if the actual http request is failed, and adding a reject method would not make much sense, rather it should be in the promise.
In your example you would not even need to call the $http service to reject the request.

Restangular error interceptor - how to pass a custom object to a controller that does not know about restangular?

I am stuck with the approach I am taking probably due to my lack of knowledge about angular promises VS restangular promises, etc.
I have an AngularJs application with TypeScript (although typescript is mostly irrelevant here and the same applies to any javascript). These are the players:
controller: it gets injected a service, through this service the controller can send a POST to an API
service: it wraps restangular. The idea is that this service does not expose any restangular functionality to the controller. It abstracts the controller from knowing how to save an item. It has a method that accepts an object and returns an angular promise.
export interface IRegistrationService {
addRegistration(model: registration.BusinessRegistration): ng.IPromise<void>;
}
Restangular error interceptor: it handles Http Responses with status 400 coming from an API because they are validation errors and transforms them in a custom object. The idea is that eventually the controller can either succeed saving an item (posting it through the service) or get a validation error (that comes from this interceptor).
This is what I have so far:
The restangular error interceptor
restangularProvider.setErrorInterceptor((response: restangular.IResponse, deferred: ng.IDeferred<any>, responseHandler: any) => {
if (response.status === 400) {
let validationError: myTsd.IApiValidationErrors = getAsValidationError(response.data);
// How to pass this validationError as an errorCallback to the controller?
//deferred.notify(validationError);
//deferred.reject(validationError); //this stops the chain
//return true; // if error not handled. But where to put the validationError?
//return false; // if error handled. But where to put the validationError?
}
});
The service that abstracts the controller from knowing anything about restangular Notice that it should return an angular promise, not a restangular promise.
public addRegistration(model: registration.BusinessRegistration): ng.IPromise<void> {
return this.restangular.all("registration")
.post<registration.BusinessRegistration>(model)
.then(() => {
console.log("Registration posted successfully");
}, (error: any) => {
//if I get the object here, how to make it available in the errorCallback of the controller caller?
}, (notify: any) => {
//if I get the object here, how to make it available in the errorCallback of the controller caller?
});
}
The controller that uses that service but knows nothing about restangular
//public static $inject = ["app.services.RegistrationService"];
//.. controller code
this.registrationService.addRegistration(this.model)
.then(() => {
console.log("model posted successfully in remote API")
}, (error: myTsd.IApiValidationErrors) => {
// if there was any validation error I need the object here
console.log(error);
});
How should I chain everything? My "only" requirements are:
the logic to create that object is in a central place like the setErrorInterceptor, and it should distinguish between http responses 400 or any other. If the response is neither 2xx or 400 it can handle the error or pass it to the service that uses restangular. It doesn't matter
the service that uses restangular must allow the controller to either succeed or have a callbackError with the custom validation error object. It abstracts the controller from everything else.
Thanks a lot!
I don't fully understand the docs here https://github.com/mgonto/restangular#seterrorinterceptor and whether there is something else other than notifying or rejecting that I could do.
Restangular's .setErrorInterceptor() is a rather odd beast, which, as far as I can gather, won't do what you want it to do.
It can be made to sense error code(s) (eg your 400) and do stuff when that condition arises, but has no further ability other than to return false (block) or return anything else (not block).
The non-blocking action allows the promise chain to take its natural, unintercepted course.
The blocking action inhibits both the error path and the success path of the promise chain.
Therefore think of .setErrorInterceptor() as a "selective blocker", not a "filter" or a "catch", and contrast it with promise.catch() behaviour, by which :
an error state can be converted to success by returning some value/object,
the error can be rethrown, or some new error can be thrown, keeping the promise chain on the error path.
The inability of .setErrorInterceptor() to propagate anything other than the original error seems to mitigate against it in favour of a named "catch handler" (eg. getAsValidationError() or a function that wraps getAsValidationError()) that can be included wherever relevant. That should give you the feature you require.
The only problem I can foresee is getting the catch handler to recognise the "400" condition - possibly simple - requires research.
Don't get too hung up on Angular promises versus Restangular. They should inter-operate.

Chain Angular $http calls properly?

I have been reading about $q and promises for days now and I seem to understand it...somewhat. I have the following situation in practice:
An $http request is made and checks whether a subsequent call can be made.
If the first call fails, return "no data", if it succeeds and says a call can be made, the second call is made, if not - "no data" again. If the second call succeeds, it returns data, if not - "no data". It looks like this (approximately, I simplified for general idea, so don't worry about the minor mistakes here):
return $http.get (something)
.then(function(allowedAccess){
if(allowedAccess){
return $http.get (somethingElse)
.then( function(result){return {data:result} },
function(error){return {data:"n0pe"} }
)
} else {
return {data:"n0pe"}
}
},
function(){ return {data:"n0pe"} });
I was told to use $q here. I don't really understand how or why I would. The $http calls are promises already.
If there is a way to make this cleaner, I don't see it. Just got done re-reading this post on the subject. Essentially, am I missing something / is there a better way to do this?
Edit: Also just re-read a tutorial on chaining promises - it doesn't handle call failures at all. Basically posting this as due diligence.
Edit 2: This is more of an elaborate on the theory I am asking about, excerpt from the first article:
This is a simple example though. It becomes really powerful if your then() callback returns another promise. In that case, the next then() will only be executed once that promise resolves. This pattern can be used for serial HTTP requests, for example (where a request depends on the result of a previous one):
This seems to be talking about chains like this:
asyncFn1(1)
.then(function(data){return asyncFn2(data)})
.then(function(data){return asyncFn3(data)})
So, if I understand correctly this a). Doesn't apply to me because I don't have a 3rd function. b). Would apply to me if I had three functions because while I run an if statement inside the first $http request, and only inside the if statement do I return another promise. So, theoretically, if I had three async functions to chain, I would need to put my if statement inside a promise?
Promises really help with code composition of making async calls. In other words, they allow you to compose your code in a similar manner to how you would compose a synchronous set of calls (with the use of chained .thens) and as if it the sync code was in a try/catch block (with .catch).
So, imagine that your HTTP calls were blocking - the logic you have would look like so:
var allowedAccess, data;
try {
allowedAccess = $http.get(something);
if (allowedAccess){
try{
var result = $http.get(somethingElse);
data = {data: result};
} catch (){
data = {data: "n0pe"};
}
} else {
data = {data: "n0pe"};
}
} catch (){
data = {data: "n0pe"};
}
return data;
You could simplify it a bit:
var allowedAccess, result;
try {
allowedAccess = $http.get(something);
var result;
if (allowedAccess) {
result = $http.get(somethingElse);
} else {
throw;
}
data = {data: result};
} catch () {
data = {data: "n0pe"};
}
return data;
And that would translate to the async version of:
return $http
.get(something)
.then(function(allowedAccess){
if (allowedAccess){
return $http.get(somethingElse);
} else {
return $q.reject(); // this is the "throw;" from above
}
})
.then(function(result){
return {data: result};
})
.catch(function(){
return {data: "n0pe"};
})
At least, this is the reasoning you could apply when composing code with branches and async calls.
I'm not saying that the version I presented is optimal or shorter - it is, however, more DRY because of a single error handling. But just realize that when you do .then(success, error) it is equivalent to try/catch over the previous async operation - this may or may not be needed depending on your specific circumstance.
This is how I would code this sort of problem:
// returns a promise that resolves some endpoint if allowed
function getDataWithAccess(allowed){
return allowed ? $http.get(someEndpoint) : $q.reject();
}
// do something with data
function handleData(data){
// do stuff with your data
}
// main chain
$http.get(accessCredEndpoint)
.then(getDataWithAccess)
.then(handleData)
.catch(function(err){
return { data: "n0pe" };
});
Yes, this is very much like New Dev's answer, however I wanted to make a point of extracting the functions into their own blocks. This makes the overall code much more readable.
$q will help reduce pyramid of calls like this:
async-call1.then(...
aysnc-call2.then(...
This blog post - http://chariotsolutions.com/blog/post/angularjs-corner-using-promises-q-handle-asynchronous-calls/ - offers a clean way of making multiple HTTP requests. Notice the cleaner approach using $q. In case you were hitting a single HTTP endpoint, using your method would have been just fine. I'd say, what you have is fine also; $q might allow greater flexibility in the future.
The blog post describes a service while using $q and the code looks cleaner.
service('asyncService', function($http, $q) {
return {
loadDataFromUrls: function(urls) {
var deferred = $q.defer();
var urlCalls = [];
angular.forEach(urls, function(url) {
urlCalls.push($http.get(url.url));
});
// they may, in fact, all be done, but this
// executes the callbacks in then, once they are
// completely finished.
$q.all(urlCalls)
.then(...
I am a beginner with promises also, so take this with a grain of salt.

Resources