reject promise when returning $http from service - angularjs

I know I could do this:
angular.module('app.services').service('Resources', function ($q) {
this.ProductData = function() {
var deferred = $q.defer();
$http.get(url)
.then(function(result) {
deferred.resolve(result);
}, function() {
defered.reject("error");
});
return deferred.promise;
}
});
But could I avoid creating deferred obj, but still be able to do both resolve and reject?
angular.module('app.services').service('Resources', function () {
this.ProductData = function() {
return $http.get(url)
.then(function (result) {
// how would I do 'deferred.resolve or deferred.reject' here?
}, function () {
// how would I do 'deferred.reject' here?
});
}
});

You are right to avoid creating a deferred and another promise for $http.
If you need to reject the promise while the actual $http call has succeeded, you'd need to return $q.reject():
return $http.get(url)
.then(function(result){
if (result.data.length === 0) return $q.reject("error");
return result;
}

Related

How can I use angularjs promise chain in a service and controller?

I found this plnkr link on the web but I need use it with 2 or 3 more ajax calls which doesn't require an argument from the first ajax call. How can I do it with error handling?
var app = angular.module("app", []);
app.service("githubService", function($http, $q) {
var deferred = $q.defer();
this.getAccount = function() {
return $http.get('https://api.github.com/users/haroldrv')
.then(function(response) {
// promise is fulfilled
deferred.resolve(response.data);
return deferred.promise;
}, function(response) {
// the following line rejects the promise
deferred.reject(response);
return deferred.promise;
});
};
});
app.controller("promiseController", function($scope, $q, githubService) {
githubService.getAccount()
.then(
function(result) {
// promise was fullfilled (regardless of outcome)
// checks for information will be peformed here
$scope.account = result;
},
function(error) {
// handle errors here
console.log(error.statusText);
}
);
});
http://plnkr.co/edit/kACAcbCUIGSLRHV0qojK?p=preview
You can use $q.all
var promises=[
$http.get(URL1),
$http.get(URL2),
$http.get(URL3),
$http.get(URL4)
];
$q.all(promises).then(function(response){
console.log('Response of Url1', response[0]);
console.log('Response of Url2', response[1]);
console.log('Response of Url3', response[2]);
console.log('Response of Url4', response[3]);
}, function(error){
});
I have forked your plunkr with $q
First you should make deferred variable local for each ajax call you want to return a promise. So, you have to create 2-3 functions (as many as your ajax calls) and keep them in an array. Then you should use:
$q.all([ajax1,ajax2,ajax3]).then(function(values){
console.log(values[0]); // value ajax1
console.log(values[1]); // value ajax2
console.log(values[2]);}); //value ajax3
example:
function ajax_N() {
var deferred = $q.defer();
http(...).then((response) => {
deferred.resolve(response);
}, (error) => {
deferred.reject(error);
});
return deferred.promise;
}
$q.all([
ajax_1,ajax_2,ajax_3
]).then(function(values) {
console.log(values);
return values;
});
Use $q.all in this case. It will call getAccount and getSomeThing api same time.
var app = angular.module("app", []);
app.service("githubService", function($http, $q) {
return {
getAccount: function () {
return $http.get('https://api.github.com/users/haroldrv');
},
getSomeThing: function () {
return $http.get('some thing url');
}
};
});
app.controller("promiseController", function($scope, $q, githubService) {
function initData () {
$q.all([githubService.getAccount(), githubService.getSomeThing()])
.then(
function (data) {
$scope.account = data[0];
$scope.someThing = data[1];
},
function (error) {
}
);
}
});

AngularJS interceptor not returning on success

I'm trying to add a popup with retry option as follows so that the user can click on that when lose the connection during a HTTP call.
angular.module('app.services', [])
.factory('httpResponseErrorInterceptor', ['$injector', '$q', function($injector, $q) {
return {
'responseError': function(response) {
var $ionicPopup = $injector.get('$ionicPopup');
if (response.status === 0) {
var confirmPopup = $ionicPopup.confirm({
title: 'No Connectivity!',
template: 'Internet not available'
});
confirmPopup.then(function(res) {
if(res) {
var $http = $injector.get('$http');
return $http(response.config);
} else {
return $q.reject(response);
}
});
}
}
};
}])
It is receiving the response from the http call, but not returning the response to the calling point. In the same way I tried the following code,
.factory('httpResponseErrorInterceptor', ['$injector', '$q', function($injector, $q) {
return {
'responseError': function(response) {
if (response.status === 0) {
var $http = $injector.get('$http');
return $http(response.config);
}
return $q.reject(response);
}
};
}])
But this one is returning the response properly to the calling point when we get the connection back. I'm not sure where I'm going wrong in the first code.
Any help/idea would be appreciated.
You should return confirmPopup.then() call.
Do like this:
return confirmPopup.then(function(res) {
if(res) {
var $http = $injector.get('$http');
return $http(response.config);
} else {
return $q.reject(response);
}
});
Example of chaining:
var promise = confirmPopup.then(function(res) {
if(res) {
var $http = $injector.get('$http');
return $http(response.config);
} else {
return $q.reject(response);
}
});
promise.then(function(success){
//HTTP SUCCESS
}, function(error){
//HTTP ERROR OR REJECT RESPONSE
});
Based on Patrick Kelleter's answer, I framed this working solution,
.factory('httpResponseErrorInterceptor', ['$injector', '$q', function($injector, $q) {
return {
'responseError': function(response) {
var $ionicPopup = $injector.get('$ionicPopup');
var $ionicLoading = $injector.get('$ionicLoading');
$ionicLoading.hide();
if (response.status === 0) {
var userInputDefer = $q.defer();
var confirmPopup = $ionicPopup.confirm({
title: 'No Connectivity!',
template: 'Internet not available',
okText: 'Retry'
});
confirmPopup.then(function(res) {
if(res) {
var $http = $injector.get('$http');
userInputDefer.resolve($http(response.config));
} else {
userInputDefer.reject($q.reject(response));
}
});
return userInputDefer.promise;
}
}
};
}]);
Edit:
Just for future reference for someone, for using the above HTTP interceptor, you have to include the factory in config as follows,
.config(['$httpProvider',function($httpProvider) {
$httpProvider.interceptors.push('httpResponseErrorInterceptor');
}]);
confirmPopup.then(function(res) {
if(res) {
var $http = $injector.get('$http');
return $http(response.config);
} else {
return $q.reject(response);
}
});
here is the problem. you are returning stuff ($http / $q) in the asynchronous callback of the confirmPopup.
the confirmPopup is async and you define a callback via ".then".
whatever you are returning there will not reach your calling point. it is the return value of the callback. which probably will not land anywhere (depending on the implementation of confirmPopup, but i doubt that it expects you to return anything there)
you will have to use your own promise and return it synchronously at the end of your callback

Why do i have to use return twice and how does it work?

I wrote a wrapper for the $http service in angular using a service. Why do i have to return bothe the http call and my result to have it available in a controller?
this is the function in the service:
this.fetchData = function(url, parameters)
{
if (parameters)
{
return $http.get(url, {params: parameters}).then(
function (response) {
console.log(response);
return response.data.data;
},
function (response) {
console.log(response);
});
}
else
{
return $http.get(url).then(
function (response) {
console.log(response);
},
function (response) {
console.log(response);
}
);
}
}
and this is where i call it in the controller:
test.fetchData('/jewelry-room/move-user.action', {user:2000, task:7}).then(
function (response) {
console.log(response);
},
function (response) {
console.log(response);
}
);
Because what you return from the factory is a promise: return $http.get().then(). Since your factory has the callback function inside then, so the promise is resolved. In this case, the function inside controller then block will not be called.
You need to return again in order to have access to the response data inside the controller.
Alternatively, just return $http.get() from the factory.
You could just return the promise to your controller by using $q:
var fetchData = function (url, params) {
if (params) {
var deferred = $q.defer();
$http.get(url, {params: params}).success(deferred.resolve).error(deferred.reject)
return deferred.promise;
} else {
var deferred = $q.defer();
$http.get(url).success(deferred.resolve).error(deferred.reject)
return deferred.promise;
}
}
Then inside your controller you can handle the promise.

How to multi-callback a promise in AngularJs with $q?

I'm using the code below in order to simplify the backend requests but I didn't catch how to call either a success method or an error method.
How can I reach the expected behavior commented in the code?
app.factory('REST', function ($http, $q, sweetAlert) {
return {
load: function (module, action, data) {
var deferred = $q.defer();
var promise = deferred.promise;
$http
.post('/api/'+module+'.php?action='+action, data)
.success(function (data) {
if(data.error)
{
sweetAlert.swal({
title: "Error",
text: data.error,
type: "warning"
});
//HERE I WANT TO CALL .error(details)
}
else
deferred.resolve(data.result);
}).error(function () {
//HERE I WANT TO CALL .error(details)
});
promise.success = function(fn) {
promise.then(fn);
return promise;
}
return promise;
}
};
});
This is the code which uses the code above:
$scope.login = function () {
$scope.loading = true;
var payload = {'credentials': $scope.logindata};
REST.load('access', 'login', payload).success(function(data) {
if(data.redirect)
$state.go(data.redirect);
$scope.loading = false;
}).error(function(data) { //THIS SHOULD BE CALLED
$scope.loading = false;
});
}
First of all, I strongly discourage you from attaching .success to the promise you are returning. This is not Promises/A-compliant, and its subtle difference from .then (as is implemented by $http) causes a lot of confusion. Just return a pure promise.
Other than that, a few things to note:
1) you don't need another $q.defer and deferred.resolve() - just chain to the original promise of $http and return the resulting promise. (see deferred anti-pattern)
2) to reject a promise - that is, to cause the .catch (not .error - see above about the subtle difference) to fire - you should return $q.reject().
All of the above produces the following:
app.factory('REST', function($http, $q, sweetAlert){
return {
load: function(module, action, data) {
// this "return" returns the promise of $http.then
return $http.post('/api/' + module + '.php?action=' + action, data)
.then(function(response) {
var data = response.data; // .then gets a response, unlike $http.success
if (data.error) {
sweetAlert.swal({
title: "Error",
text: data.error,
type: "warning"
});
//HERE I WANT TO CALL .error(details)
return $q.reject(data.error);
}
return data.result; // what you would have "resolved"
});
}
};
})
Then, as I said above, use the .then/.catch as you would with promises:
$scope.login = function () {
$scope.loading = true;
var payload = {'credentials': $scope.logindata};
REST.load('access', 'login', payload)
.then(function(data) {
if(data.redirect)
$state.go(data.redirect);
$scope.loading = false;
})
.catch(function(error) {
$scope.loading = false;
});
}
Update yr code as below
app.factory('REST', function ($http, $q, sweetAlert) {
return {
load: function (module, action, data) {
var deferred = $q.defer();
$http.post('/api/'+module+'.php?action='+action, data)
.success(function (data) {
if(data.error)
{
sweetAlert.swal({
title: "Error",
text: data.error,
type: "warning"
});
//HERE I WANT TO CALL .error(details)
deferred.reject(data.error);
}
else{
deferred.resolve(data.result);
}
})
.error(function (error) {
//HERE I WANT TO CALL .error(details)
deferred.reject(error);
});
return defferred.promise;
}
};
});
for yr controller
$scope.login = function () {
$scope.loading = true;
var payload = {'credentials': $scope.logindata};
REST.load('access', 'login', payload).then(
function(data) {
if(data.redirect)
$state.go(data.redirect);
$scope.loading = false;
},
function(error) {
$scope.loading = false;
});
}

Setting up a service with $q

I am trying to set up a service with $q (and resangular), so in a controller I could then call
myservice.get.then(function(data){
//use data
}
Right now I have
.service(urlService,
function($q, Restangular) {
this.get = function(newURl) {
return $q(function(resolve, reject) {
Restangular.one(newURl).get()
.then(resolve)
.catch(reject);
});
};
}
If I inject into a controller and call
urlService.get(newUrlObject).then(function(data){
console.log(data);
});
this does not seem to work. I could use some help getting this right, thanks!
You have to call resolve and reject:
return $q(function(resolve, reject) {
Restangular.one(newURl).get()
.then(function(data) {
resolve(data)
})
.catch(function(whatever) {
reject(whatever);
});
But Restangular.one(newURl).get() already returns a promise, so wrapping the code into another deferred doesn't make much sense. Id rather use the promise directly:
this.get = function(newURl) {
return Restangular.one(newURl).get();
};
May be you should use $q.refer, like this:
.service(urlService,
function($q, Restangular) {
this.get = function(newURl) {
var deferred = $q.defer();
Restangular.one(newURl).get()
.then(function() {
deferred.resolve();
})
.catch(function(){
deferred.reject();
});
return deferred.promise();
};
}
Reference: https://docs.angularjs.org/api/ng/service/$q

Resources