I'm dealing with code like this:
return promise.then(function(url) {
return $http.get(url, data);
}).then(function (response) {
return response.data;
}).catch(function (response) {
return $q.reject(response);
});
All I want to do is to reject the promise in the "catch" block so the exception is logged by my custom implementation of $exceptionHandler.
According to my understanding of the documentation, the line below is supposed to rethrow the exception but it is not doing it. Thus, $exceptionHandler never handlers the exception:
return $q.reject(reason); // why isn't it rethrowing?
Chances are I'm misunderstanding the documentation.
The error is re throwing - as controller can catch error and displays it.
angular.module('app', [])
.controller('app', function($scope, service) {
$scope.error = {}
service
.load('foo')
.catch(function(error) {
angular.extend($scope.error, {
reason: error
});
});
})
.service('service', function($http, $q) {
var promise = function promise() {
var d = $q.defer();
d.reject('some reason')
return d.promise;
}();
this.load = function(url) {
return promise
.then(function() {
return $http.get(url);
})
.then(function(response) {
return response.data;
})
.catch(function(reason) {
return $q.reject(reason);
});
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app='app'>
<div ng-controller='app'>error: {{ error.reason }}</div>
</div>
Related
We have the following code in place for form validation:
$scope.masterModel.$save().then(function (data) {
$scope.masters.data.push(data);
$location.path('/master/edit/' + data.id);
}).error(function (data) {
$scope.errors = data.data;
});
Now we added code to generally catch code 500 server errors on a global level to the app.js
app.config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push(function ($injector) {
return {
'responseError': function (rejection) {
// do something on error
if (rejection.status === 500) {
var angularModalService= $injector.get("ModalService");
angularModalService.showModal({
templateUrl: "templates/common/session.html",
controller: "ModalController"
}).then(function (modal) {
modal.element.modal();
modal.close.then(function (result) {
if (result) {
}
});
});
}
}
};
});
}]);
As soon as we add this code, the error callback in the first code does not work anymore.
I think we have to somehow propagate the error in the responseError callback, but how does that work?
AngularJS Version is 1.5.11
You need to "reject the rejection" in the interceptor and return it in order for the error to be propagated:P
var app= angular.module('MyApp', []);
app.controller('Controller', function($scope, $http, $q) {
$http.get("http://www.example.invalid/fake.json")
.then(function(response) {
console.log("success");
}, function(error) {
console.log("controller error handler");
});
});
app.config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push(function ($injector, $q) {
return {
'responseError': function (rejection) {
console.log("interceptor error handler");
// do something on error
if (rejection.status === 500) {
// do something...
}
return $q.reject(rejection);
}
};
});
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyApp">
<div ng-controller="Controller">
</div>
</div>
As you can see, I added the line
return $q.reject(rejection);
at the end of your interceptor. You can check the console and see that now both messages are logged.
In one of my new projects I am using AngularJS $resources for http calls. I have requirement to show local data (from SQLite) when there is no internet connection.
I have clustered my obstacles by the following steps, and created sample code down below
Step 1: On click of the button call the $resource //Done
Step 2: Before sending the request check if machine connected to
network //Done
Step 3: If network connected, get the response //Done
Step 4: If no network then abort the request//Done
Step 5: If no network respond the local data to the $resource promise -- Not sure how to do that.
var servicetest = angular.module('servicetest', ['ngResource'])
.factory('interceptors' ,function($rootScope, offlineCntrl){
return {
'request':function(request){
if(!offlineCntrl.isOnline()){
//there wont be a server call
request.url='';
//Abort the request
request.timeout;
}else{
return request;
}
},
'response':function(response){
if(!offlineCntrl.isOnline()){
offlineCntrl.assignLocal(request).then(function(data){
//Get the local data here.
// Have to pass this data to my response
// At this stage I am getting an error attached below
response.data = data;
return response;
})
}else{
return response;
}
}
}
})
.config(function($httpProvider){
$httpProvider.interceptors.push('interceptors');
})
.factory('offlineCntrl', function($q, $timeout){
return {
isOnline: function(){
return navigator.onLine;
},
assignLocal:function(request){
var defer = $q.defer();
$timeout(function(){
defer.resolve({title:"local", body:"Local local data context"});
})
return defer.promise;
}
}
})
.factory('CloudSerivce', function($resource){
return $resource('https://jsonplaceholder.typicode.com/posts/:id', {
id:'#id'
}, {
get:{
method:"GET",
isArray:false,
cancellable: true
}
})
})
.controller('mainCntrl', function($scope, CloudSerivce){
$scope.data = {};
$scope.getCloudService = function(){
CloudSerivce.get({id:1}).$promise.then(function(data){
//It should receive local data if there is no network
$scope.data = data;
}, function(err){
console.log(err)
})
}
})
<html ng-app="servicetest">
<body ng-controller='mainCntrl'>
<h1>{{data.title}}</h1>
<p>{{data.body}}</p>
<button ng-click='getCloudService()'>
GET ITEMS
</button>
<h4>Try the button disabling your internet connection</h4>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.7/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.7/angular-resource.min.js"></script>
</html>
I am getting an error when I try to return the response with deferred promise event.
TypeError: Cannot read property 'headers' of undefined
at f (angular.js:7520)
at C (angular.js:10924)
at angular.js:11010
at h.$eval (angular.js:11930)
at h.$digest (angular.js:11756)
at a.$apply (angular.js:12036)
at HTMLButtonElement.<anonymous> (angular.js:17871)
at angular.js:2612
at q (angular.js:309)
at HTMLButtonElement.c (angular.js:2611)
I can do it by bubbling up a $rootScope emitter and catch it within the controller, but I believe the easiest way is to hack the ngResource (CloudService $promise event) response after aborting the request.
So that I can keep the controller getCloudService function as it is, which can work on offline and online mode.
Need to do something like that
.factory('interceptors' ,function($rootScope, offlineCntrl){
return {
'request':function(request){
if(!offlineCntrl.isOnline()){
//there wont be a server call
request.url='';
//Abort the request
request.timeout;
}else{
return request;
}
},
'response':function(response){
const deferred = $q.defer();
if(!offlineCntrl.isOnline()){
offlineCntrl.assignLocal(request).then(function(data){
deferred.resolve(data);
});
}else deferred.resolve(response);
return deferred.promise;
}
}
})
You made a mistake here!
$scope.getCloudService = function(){
CloudSerivce.get({id:1}).$promise.then(function(response){
//It should receive local data if there is no network
$scope.data = response.data;//modified
})
}
Also to handle the error case you should be using somehting like this?
$scope.getCloudService = function(){
CloudSerivce.get({id:1}).$promise.then(function(response){
//It should receive local data if there is no network
$scope.data = response.data;//modified
}),function(error){
//ERROR HANDLING
});
}
The following work around worked out.
var servicetest = angular.module('servicetest', ['ngResource'])
.factory('interceptors' ,function($rootScope, offlineCntrl, $q){
return {
'request':function(request){
if(!offlineCntrl.isOnline()){
//there wont be a server call
request.url='';
//Abort the request
request.timeout;
}else{
return request;
}
},
'response':function(response){
if(!offlineCntrl.isOnline()){
var deferred = $q.defer();
offlineCntrl.assignLocal(request).then(function(data){
//Get the local data here.
// Have to pass this data to my response
// At this stage I am getting an error attached below
response = data;
return deferred.resolve(response);
})
return ( deferred.promise );
}else{
return response;
}
}
}
})
.config(function($httpProvider){
$httpProvider.interceptors.push('interceptors');
})
.factory('offlineCntrl', function($q, $timeout){
return {
isOnline: function(){
return navigator.onLine;
},
assignLocal:function(request){
var defer = $q.defer();
$timeout(function(){
defer.resolve({title:"local", body:"Local local data context"});
})
return defer.promise;
}
}
})
.factory('CloudSerivce', function($resource){
return $resource('https://jsonplaceholder.typicode.com/posts/:id', {
id:'#id'
}, {
get:{
method:"GET",
isArray:false,
cancellable: true
}
})
})
.controller('mainCntrl', function($scope, CloudSerivce){
$scope.data = {};
$scope.getCloudService = function(){
CloudSerivce.get({id:1}).$promise.then(function(data){
//It should receive local data if there is no network
$scope.data = data;
}, function(err){
console.log(err)
})
}
})
<html ng-app="servicetest">
<body ng-controller='mainCntrl'>
<h1>{{data.title}}</h1>
<p>{{data.body}}</p>
<button ng-click='getCloudService()'>
GET ITEMS
</button>
<h4>Try the button disabling your internet connection</h4>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.7/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.7/angular-resource.min.js"></script>
</html>
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) {
}
);
}
});
I have set up a PouchDb service in Angular/Ionic which works fine within the service, but fails when I try to pass the data I retrieve from PouchDB service to my controller.
this is my service
angular.module('myApp', [])
.service('DBService', function($q) {
var items;
var db;
var self = this;
this.initDB = function() {
return db = new PouchDB('simpleDB', {
adapter: 'websql'
});
};
this.storeData = function() {
return $q.when(
db.put({
_id: 'mydoc',
title: 'some text'
}).then(function (response) {
// handle response
console.log('done')
}).catch(function (err) {
console.log(err);
})
)
};
this.getData = function(){
return $q.when(
db.get('mydoc').then(function (doc) {
// handle doc
console.log(doc); // I see the data in console
}).catch(function (err) {
console.log(err);
})
)
}
})
and this is the controller
angular.module('myApp', [])
.controller('mainCtrl', function($scope, DBService, $q) {
$scope.getData = function(){
DBService.initDB()
DBService.getData().then(function(data){
console.log(data)
})
}
when I use then() I get error TypeError: Cannot read property 'then' of undefined.
Can anyone help me figure out how I can pass the data correctly from my service to the controller?
I was lucky to find the error quickly! I was not returning the data in my service so I changed it to
this.getData = function(){
return $q.when(
db.get('mydoc').then(function (doc) {
// handle doc
return doc; // I added this
console.log(doc); // I see the data in console
}).catch(function (err) {
console.log(err);
})
)
}
and the reason for the then() returning undefined was that accidentally I had omitted return $q.when( from the second line of the this.getData function! Now I have my data in the controller just as I needed!
Now I have tried to display the response which is returned from the service to the controller..When I am trying to display the error its not working.The below is the code which i have tried
var app=angular.module('httpExample',[]);
app.controller("FetchController",["$scope","clickevent",function($scope,clickevent){
clickevent.fetch().then(function(response){
$scope.req=response;
console.log(angular.toJson(response));
}).error(function(error){
console.log(error);
});
}]);
app.service("clickevent",['$http',function($http){
var clickevent={
fetch:function(){
var prom=$http({method:"GET",url:"http://www.w3schools.com/angular/customers.php"}).
success(function(response){
return response;
}).
error(function(err){
return err;
});
return prom;
}
};
return clickevent;
}]);
Promises don't have an error() function. They have a catch() function. success() and error() are specific to promises returned by $http, and those deprecated functions don't allow chaining like then() and catch() allow.
Here's how your code should look like:
var app = angular.module('httpExample',[]);
app.controller("FetchController",["$scope", "clickevent", function($scope, clickevent){
clickevent.fetch().then(function(data) {
$scope.req = data;
console.log(angular.toJson(data));
}).catch(function(error) {
console.log(error);
});
}]);
app.service("clickevent",['$http', '$q', function($http, $q){
var clickevent = {
fetch: function() {
var prom = $http({method:"GET",url:"http://www.w3schools.com/angular/customers.php"}).
then(function(response) {
return response.data;
}).
catch(function(response) {
return $q.reject(response.data);
});
return prom;
}
};
return clickevent;
}]);