Force rejecting Angular $http call - angularjs

I am using $http to make a call. Based on the successful result of the call I may decided to throw an error/reject and have it trickle down to the next call as an error. However if an error is thrown it just halt the process. How can I force the $http promise to reject without wrapping it in some $q code?
// A service
angular.module('app').factory('aService', function ($http, config) {
return {
subscribe: function (params) {
return $http({
url: '...'
method: 'JSONP'
}).then(function (res) {
// This is a successful http call but may be a failure as far as I am concerned so I want the calling code to treat it so.
if (res.data.result === 'error') throw new Error('Big Errror')
}, function (err) {
return err
})
}
}
})
// Controller
aService.subscribe({
'email': '...'
}).then(function (result) {
}, function (result) {
// I want this to be the Big Error message. How do I get here from the success call above?
})
In the above code I would like the Big Error message to end up as a rejected call. However in this case it just dies with the error. This is how I handle things in say Bluebird but it's a no go here.

Ti continue the Chain in a rejected state just return a rejected promise $q.reject('reason') from your $http result something like
$http.get(url).then(
function (response){
if(something){
return $q.reject('reason');
}
return response;
}
)
That way you'll get a a rejected promise and can react to it even when the api call is successful.

Related

Get result of rest request instead of response?

I have the following function in my controller.
RestRequestsSrvc.getUserDetail()
.then(
function (response) {
$scope.user.userDetail = response;
},
function (error) {
// TODO
});
If I type
console.log(RestRequestsSrvc.getUserDetail());
the console logs a promise. I want to set a variable the the response. How can I modify my code so that I get the response instead of a promise?
Return a promise because your request is async.
You should wait the response,
Putting the console.log inside the callback function should print your info.
RestRequestsSrvc.getUserDetail()
.then(
function (response) {
$scope.user.userDetail = response;
console.log(response);
},
function (error) {
// TODO
});
You can do the console.log into the promise .then
RestRequestsSrvc.getUserDetail()
.then(
function (response) {
$scope.user.userDetail = response;
console.log(response);
},
function (error) {
// TODO
});
The thing is that when you call the function it will be executed but will not wait for the result, that's why you get a promise. The .then stuff is called once the request is done executing. That's where you handle your success or error callbacks.
Requests to the server are asynchronous, meaning that you must handle the response inside the callback.
You could use async false flag but this is not recommended if you have independent modules that executed later in the code.

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

chaining ngResource $promise success and errors

I'd like to know if it's possible to handle the $promise returned by ngResource on multiple levels so that the code is DRY
here is a simple example
aService = function(aResource) {
var error, success;
success = function(response) {
console.log('Service Success');
};
error = function(response) {
console.log('Service Error');
};
this.servicePostReq = function() {
return aResource.save().$promise.then(success, error);
};
return this;
angular.module('app.service').factory('aService', ['aResource', aService]);
this works fine so far... it Service Success when response is OK and it Service Error when response is not OK
but when I add a controller that use this aService like following
aController = function(aService) {
var error, success;
success = function(response) {
console.log('Controller Success');
};
error = function(response) {
console.log('Controller Error');
};
this.controllerPostReq = function() {
aService.servicePostReq().then(success, error);
};
return this;
};
angular.module('app.controller').controller('aController', ['aService', aController]);
the controller always success...
so if the request return success the output is
Service Success
Controller Success
and if the request fails the output is
Service Error
Controller Success
how do I chain the promise so that I don't have to add the code handled in the service for every controller that use the service ?
The problem is your service. Change this:
this.servicePostReq = function() {
return aResource.save().$promise.then(success, error);
};
To this:
this.servicePostReq = function() {
return aResource.save().$promise.then(success);
};
Explanation:
Since your service returns aResource.save().$promise.then(success, error), it's returning a new promise with an error handler included. Later, in your controller, you add onto the chain like this.
aService.servicePostReq().then(success, error);
The complete promise chain at this point looks if you expand it out:
return aResource.save().$promise
.then(successFnFromService, errorFnFromService)
.then(successFnFromController, errorFnFromController);
Since you catch the error from aResource.save() with errorFnFromService, the promise chain is basically "clean" at this point and it will just continue with the next then.
By removing the first error handler, you allow the error to be caught later on.
A better way (in general) to handle errors in promise chains would be to use a single .catch() at the end of the chain.
Consider this bad code (try running on your browser console):
new Promise(
function(resolve, reject){
reject('first');
}).then(
function(result) {
console.log('1st success!', result);
return result;
},
function(err) {
console.log('1st error!', err);
return err;
}
).then(
function(result){
console.log('2nd success!', result);
},
function(err){
console.log("2nd error!", err);
}
);
Output:
1st error! first
2nd success! first
Better way:
new Promise(
function(resolve, reject){
reject('first');
}).then(function(result) {
console.log('1st success!', result);
return result;
}).then(function(result){
console.log('2nd success!', result);
// catch error once at the end
}).catch(function(err){
console.log("error!", err);
});
Output:
error! first
Try both of those in browser console, and change reject to resolve to see how it affects the output.
add a dependency on the $q and use $q.reject to control the execution...
in your example you need a $q.reject in the aService.error method
as mentioned here in the $q docs
reject(reason);
Creates a promise that is resolved as rejected with the specified reason. This api should be used to forward rejection in a chain of promises. If you are dealing with the last promise in a promise chain, you don't need to worry about it.
When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of reject as the throw keyword in JavaScript. This also means that if you "catch" an error via a promise error callback and you want to forward the error to the promise derived from the current promise, you have to "rethrow" the error by returning a rejection constructed via reject.
To properly chain promises, both success and error handlers should return some value. The return values are automatically wrapped in a new promise for you. This means that in the case of errors, you must return a rejected promise using $q.reject.
So your service should look like this:
aService = function($q, aResource) {
var error, success;
success = function(response) {
// important! return a value, handlers down the chain will
// execute their success handlers and receive this value
return 'Service Success';
};
error = function(response) {
// important! return a REJECTION, handlers down the chain will
// execute their error handlers and receive this value
return $q.reject('Service Error');
};
this.servicePostReq = function() {
return aResource.save().$promise.then(success, error);
};
return this;
angular.module('app.service').factory('aService', ['$q', 'aResource', aService]);

$http and promises and POST

Given code like the following:
function webCall() {
return $http({ method: "POST",
url: "http://destined/to/fail", data: {param1: 1})
.success(function(data, status) { return { test: "success!";} } )
.error(function (data, status) {
return {errorMessage: "Totally failed!"};
});
It is my understanding that if I call .then() on the returned promise like so:
var myPromise = webCall().then(
function(response){
console.log(response.test);
},
function(reason) {
console.log(reason.errorMessage);
});
that the returned value from the appropriate .success() and .error() callbacks is passed along to the .then() callbacks.
I am not seeing the behavior I expect however. Using GET it works as expected. With POST, not so much. Is my assumption that it should act like normal deferred \ promise accurate? Where is it documented (other than the source)
$http() returns a promise, but what you're returning is actually the result of .error(), which isn't a promise (at least it's not documented as such).
To return a promise, you have use then()...
function webCall() {
return $http({ method: "POST",
url: "/destined/to/fail", data: {param1: 1})
.then(function(response) {
// if you need to access the data here, use response.data
return { test: "success!" };
}, function (response) {
throw { errorMessage: "Totally failed!" };
});
}
Note the use of throw to reject the returned promise, because if you simply return from the error callback, it actually resolves resulting promise.
The reason it doesn't work as you expect is because the .success and .error handlers return the original promise and it does nothing with the return value from the success/error handlers. You can see that from the source code for $http here.
promise.success = function(fn) {
promise.then(function(response) {
fn(response.data, response.status, response.headers, config);
});
return promise;
};
You can see that once the promise is resolved (when the web call returns), it will call your sucess handler, and then do nothing with it.
When you chain this with other handlers it is the result of the original promise that is passed along to the other handlers.

AngularJS Resource error callback being called with a promise

I've been writing a service in AngularJS to save some data and, if it fails, alert the user. However, after I create my resource and call $save:
myResource.$save(function(success) {
console.log(success);
}, function(error) {
console.log(error);
});
I expect the error callback's argument to be an object with data, status, headers, etc., but all I get is an object with a "then" function. I tried to mock it up in JSFiddle:
http://jsfiddle.net/RichardBender/KeS7r/1/
However, this example works as I originally expected. I yanked this JSFiddle example and put it in my project and it has the same problem I originally described, despite that as far as I can tell everything else is equal. Does anyone have any idea why this might be? My project was created with Yeoman/Bower/Grunt but I can't see why those things would make a difference here.
Thanks,
Richard
I solved the problem. The error was in my HTTP interceptor, where upon an error code, I was accidentally returning $q.reject(promise) rather than $q.reject(response).
The bugged version:
.factory('httpInterceptor', function($q) {
return function(promise) {
return promise.then(
// On success, just forward the response along.
function(response) {
return response;
},
function(response) {
// ... where I process the error
return $q.reject(promise);
}
);
};
The fixed version:
.factory('httpInterceptor', function($q) {
return function(promise) {
return promise.then(
// On success, just forward the response along.
function(response) {
return response;
},
function(response) {
// ... where I process the error
return $q.reject(response);
}
);
};
-Richard

Resources