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;
});
}
Related
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) {
}
);
}
});
//the controller that creates the datatable
app.controller('AdminListCtrl', function ($scope, $compile, DTOptionsBuilder, DTColumnBuilder, adminService) {
var vm = this;
function stateChange(iColumn, bVisible) {
console.log('The column', iColumn, ' has changed its status to', bVisible);
}
//vm.dtOptions = DTOptionsBuilder.fromSource('http://localhost/api-v1/admin')
vm.dtOptions = DTOptionsBuilder.fromFnPromise(function() {
return adminService.loadAdmin();
})
.withPaginationType('full_numbers')
.withOption('createdRow', createdRow)
// Add Bootstrap compatibility
.withBootstrap()
// Active ColVis plugin
.withColVis()
// Add a state change function
.withColVisStateChange(stateChange)
// Exclude the last column from the list
.withColVisOption('aiExclude', [2])
// Add Table tools compatibility
.withTableTools('scripts/vendor/datatables/TableTools/swf/copy_csv_xls_pdf.swf')
.withTableToolsButtons([
'copy',
'print', {
'sExtends': 'collection',
'sButtonText': 'Save',
'aButtons': ['csv', 'xls', 'pdf']
}
]);
//adminService to request for all administrators
app.factory('adminService', ['ApiService', function (ApiService) {
return {
loadAdmin: function () {
ApiService.get("admin").then(function (response) {
if (response) {
if (response.success === true) {
return response;
}else{
console.log(response);
}
}else {
console.log('error request ');
}
});
}
};
}]);
//apiservice to interact with api
app.factory('ApiService', function ($http, $q, $localStorage) {
return {
get: function (apiresource) {
var returnData = $q.defer();
$http({
url: api + apiresource,
method: 'GET',
headers: {"Auth-Token": $localStorage.user_data.auth_token}
})
.success(function (data) {
returnData.resolve(data);
})
.error(function (error) {
returnData.resolve();
});
return returnData.promise;
}};
});`enter code here`
When ever I am in that view it throws this errorCannot read property 'then' of undefined. I am following examples from these two sources
http://www.revillweb.com/angularjs-by-example/4-sharing-data-with-angularjs-services/
http://l-lin.github.io/angular-datatables/#/withPromise
You need to return promise object (result of ApiService.get("admin") call) from loadAdmin method.
Also make sure you don't "swallow" rejections inside of the then (in console.log branches) - what happens when you unintentionally handle errors by not passing it further. For this return rejected promise or simply throw error, so that rejection will propagate further down the promise chain:
app.factory('adminService', ['ApiService', function (ApiService) {
return {
loadAdmin: function () {
return ApiService.get("admin").then(function (response) {
if (response) {
if (response.success === true) {
return response;
} else{
console.log(response);
throw response;
// or custom error object: throw {message: 'Error loadAdmin', response}
}
} else {
console.log('error request ');
throw new Error('error request');
}
});
}
};
}]);
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
This the factory.
latModule.factory('latSvc',
[
"$http", "$scope", "$q",
function($http, $scope, $q) {
console.log("Enter latUserReportDateSvc");
return {
getPromiseForUserReportDate: function () {
$scope.userId = "bpx3364";
var deferred = $q.defer();
$http.get('/api/UserReportStatusApi', { 'userId': $scope.userId }).then(function(reponse) {
deferred.resolve(reponse);
},
function(error) {
deferred.reject(error);
});
return deferred.promise;
},
getPromiseForLocation: function () {
$scope.userId = "bpx3364";
var deferred = $q.defer();
$http.get('api/UserAccountApi/', { 'userId': $scope.userId }).then(function (reponse) {
deferred.resolve(reponse);
},
function (error) {
deferred.reject(error);
});
return deferred.promise;
},
getPromiseForErrorSummary: function (userInfoVm) {
console.log("latErrorSummarySvc getErrorCounts, userInfo: ", userInfoVm);
$scope.userId = "bpx3364";
$scope.serviceTypeCode = 4;
var deferred = $q.defer();
$http.get('/api/UserReportStatusApi', { 'userId': $scope.userId, 'serviceTypeCode': $scope.serviceTypeCode }).then(function (reponse) {
deferred.resolve(reponse);
},
function (error) {
deferred.reject(error);
});
return deferred.promise;
}
};
}
]);
This is the controller
latModule.controller("dashboardController",
["$scope","latSvc",
function ($scope,latSvc) {
console.log("enter dashboard controller");
console.log("scope: ", $scope);
console.log("homeUserInfo: ", $scope.homeLatUserInfo);
var dashboardUserReportDate = function() {
latSvc.getUserReportDateInfo().then(
function(response) {
$scope.dashboardUserReportDateData = response;
}, function(error) {}
);
};
dashboardUserReportDate();
var dashboardErrorCounts = function() {
latSvc.getPromiseForErrorSummary($scope.homeLatUserInfo).then(
function(response) {
$scope.dashboardErrorCountsData = response;
},
function (error) { }
);
};
dashboardErrorCounts();
var dashboardAtmCount = function() {
latSvc.getPromiseForLocation().then(
function(response) {
$scope.dashboardAtmCountData = response;
}, function(error) {}
);
};
dashboardAtmCount();
}]);
after running this code I am getting an unknown provider error while I am trying to implement this promise concept.Because while I was calling through service with out resolving promise and without using then the url was getting hit multiple times.
You can't use / inject $scope into a service.
But as far as I understand your code you should be fine using local variables for your http request query parameters.
And Vineet is right - you should return the $http.get() directly (it's already a promise).
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;
}