How to do error handling from .config block using httpProvider - angularjs - angularjs

I would like to intercept all http error messages and give the user visual feedback. For this, I want to do something like this
.config(function($httpProvider) {
$httpProvider.interceptors.push(function($q) {
return {
'responseError': function(rejection) {
// show visual feedback or emit an event.
return $q.reject(rejection);
...
However, I can't inject $rootScope (to do a $rootScope.$emit()) since this is a config block. Nor is it possible to inject angular-growl or similar package, for the same reasons. How does one address this use case?

You can inject $rootScope together with $q:
.config(function($httpProvider) {
$httpProvider.interceptors.push(function($q, $rootScope) {
return {
'responseError': function(rejection) {
// show visual feedback or emit an event.
$rootScope.$emit( ... );
return $q.reject(rejection);

Try something like this
angular.module('interceptor.http', [])
.config(function ($httpProvider) {
$httpProvider.responseInterceptors.push('errorInterceptor');
})
.factory('errorInterceptor', function ($q, $rootScope) {
return function (promise) {
return promise.then(function (response) { },
function (response) {
$rootScope.$broadcast("error",{statusCode : 'error'});
return $q.reject(response);
});
};
});

... You can inject $rootScope. I don't know what you're talking about. However, if you'd like to inject dependencies at run-time that would otherwise require a service that would create a circular dependency, you can use the $injector service within config blocks like so:
// ... other angular code
.config(function($httpProvider){
$httpProvider.interceptors.push(function($q, $injector){
return {
responseError: function(arg1, arg2, argWhatever){
// This isn't run until there's actually an error (everything has already been configured)
// This is totally allowed
$http = $injector.get('$http');
// ... do whatever with your service
return $q.reject(/*something here*/);
}
};
});
});

Related

ngResource $promise error handling?

I have the following controllers which all the ngResource services to get data.
.controller('venueCtrl', function ($scope, $stateParams, VenueService) {
$scope.venue = VenueService.get({ id: $stateParams.id });
})
.controller('tomorrowCtrl', function ($scope, EventService) {
var evts = EventService.query({ period: "Tomorrow" });
evts.$promise.then(function (response) { $scope.events = response; });
}).....
Now I need to add error handling (for example, display an alert box) for the error situations, e.g., no network, web service failed, etc. How to add the code to handle the errors?
You can use Interceptors this way:
angular.module('ngRessourceErrorsHandler', [])
.config(function($resourceProvider) {
angular.forEach($resourceProvider.defaults.actions, function(action) {
action['interceptor'] = {
responseError: function(httpResponse) {
//Do whatever you want here !
}
};
})
});
Please try this and let me know if this works for you. Don't forget to add ngRessourceErrorsHandler dependency to your module or just use config directly.

Best way to handle/log resource errors in angular

I'm trying to setup solid error handling within my angular web application. I have a lot of various api calls using various sources, within many different controllers and directives. Ideally I'd like to handle resource errors in a manner like this:
$scope.loadData = function () {
$scope.loaded = false;
dataFactory.getData($scope.RecordId)
.$promise
.then(function (resp) {
$scope.Data = resp;
$scope.loaded = true;
}, function (resp) {
$scope.loaded = true;
$scope.handleResourceError(resp);
})
};
Since I inject $scope in all controllers/directives that would need this functionality, am I able to define a Resource Error method that would be accessible anywhere $scope is injected?
Interceptors might help you. instead of writing error handler function every time, you can just bind your function to root scope and call it form the "responseError" interceptor.
Here i bind a function to open the error model to $rootScope and i'm calling it form the interceptor. you can find the example below.
Sample code:
(function (global) {
"use strict";
angular.module("TestAPp").factory('httpInterceptor', ["$rootScope", "$q", function ($rootScope, $q) {
return {
responseError: function (response) {
/* Open Error model and display error */
$rootScope.openErrorModel(response);
/* reject deffered object so that it'll reach error block , else it'll execute success function */
return $q.reject(response);
}
};
}]);
}(this));
//registering interceprtor
(function (global) {
"use strict";
angular.module("TestApp", [])
.config([ '$httpProvider',function ($httpProvider) {
/* Register the interceptor */
$httpProvider.interceptors.push('httpInterceptor');
}]);
}(this));
PS: My openErrorModel definition
$rootScope.openErrorModel = function (error) {
$rootScope.errorMessage = error.data;
$('#errorModal').modal('show');
};
You can refer to Error Handling in angular for more info.

How do I preform a redirect in an angular interceptor?

I am pretty new to angular, and I had a question on the best way to handle a redirect within an interceptor.
I have certain pages in my app that I should only be able to access if I have an account selected. So if an account is not selected I want the route the user to page to select the account.
The following is my failed attempt:
// within config
$httpProvider.interceptors.push(function($q, $injector){
return {
'request': function(config) {
var state = $injector.get('$state');
if(state.is('user.list')) {
var accountService = $injector.get('AccountService');
if(!accountService.accountSelected()){
// cancel the current request
var defer = $q.defer();
defer.resolve();
config.timeout = defer.promise;
state.go('account.select');
}
}
return config;
}
}
});
This is causing an infinite loop for me. For some reason when state.go fires -- and it gets re-intercepted the state is still "user.list"
Note: I am using ui-router, angular 1.2.6
Another Note: The other place I thought of putting this was in a state.resolve block.
do it like this
$injector.get('$state').transitionTo('public.login');
full code below
var interceptor = ['$location', '$q', '$injector', function($location, $q, $injector) {
function success(response) {
return response;
}
function error(response) {
if(response.status === 401) {
$injector.get('$state').transitionTo('public.login');
return $q.reject(response);
}
else {
return $q.reject(response);
}
}
return function(promise) {
return promise.then(success, error);
}
}];
$httpProvider.responseInterceptors.push(interceptor);
I think you may want to try $location.path(destination) instead of $state.go(destination).
You'd better not put your redirect logic under 'request' block as accountService.accountSelected() may also make a request. here is the doc of $http interceptors
If you only have several routes which need to check account, it is better to put it in state.resolve block as you point out or put it in corresponding controller.

Abstracting $http calls into service

I'm wondering what the best way to abstract $http calls into an angularjs service is. I've done a bit of research and this seems to be the most common way:
app.factory('myService', function($http) {
return {
getFoo: function() {
return $http.get('foo.json').then(function(result) {
return result.data;
});
}
}
});
app.controller('MainCtrl', function($scope, myService) {
//the clean and simple way
$scope.foo = myService.getFoo();
}
But the problem with this approach is I can't figure out how to do anything on .error.
I'd prefer to have my .success and .error callbacks inside my controller.
Is there a way to abstract the http call inside the service whilst maintaining .error and .success callbacks inside the controller?
Thank you.
You can still use the on success/error calls.
The method you've highlighted returns a "Promise" object. A good thing about promises is that they are chainable.
So say you wish to respond to a $http request error in your controller:
app.factory('myService', function($http) {
return {
getFoo: function() {
return $http.get('foo.json').then(function(result) {
return result.data;
});
}
}
});
app.controller('MainCtrl', function($scope, myService) {
//the clean and simple way
$scope.foo = myService.getFoo().then(function(){
//Do something with successful response
}, function(){
//Do something with unsuccessful response
});
}
NOTE: This following next section no longer holds true. Promises used in templates are no longer automatically resolved to its values when the promise resolves.
You should also understand why assigning $scope.foo works in your templates. AngularJS has a bit of magic that will resolve any promises to the object you need in a template. So while your template might reference foo.bar and the output will be correct, whats actually happening in the background is that the template is waiting for the promise to be fulfilled before rendering that part of the template.
Also, another gotcha is to remember to return an rejected promise if you're handling the error somewhere up in the chain.
For example:
app.factory('myService', function($http, $q) {
return {
getFoo: function() {
return $http.get('foo.json').then(function(result) {
return result.data;
}, function(result){
//I'm doing something here to handle the error
return $q.reject(result);
});
}
}
});
app.controller('MainCtrl', function($scope, myService) {
//the clean and simple way
$scope.foo = myService.getFoo().then(function(){
//Do something with successful response
}, function(){
//Do something with unsuccessful response
});
}
If we didn't return a rejected promise in the service, the controller's 'success' code path will be run instead of the reject path.

AngularJS: need to fire event every time an ajax call is started

I am trying to fire an event on $rootScope every time an ajax call is started.
var App = angular.module('MyApp');
App.config(function ($httpProvider) {
//add a transformRequest to preprocess request
$httpProvider.defaults.transformRequest.push(function () {
//resolving $rootScope manually since it's not possible to resolve instances in config blocks
var $rootScope = angular.injector(['ng']).get('$rootScope');
$rootScope.$broadcast('httpCallStarted');
var $log = angular.injector(['ng']).get('$log');
$log.log('httpCallStarted');
});
});
The event 'httpCallStarted' it's not being fired. I suspect that it's not correct to use $rootScope or any other instance service in config blocks. If so, how can I get an event everytime an http call is starting, without having to pass a config object in every time I am making a call?
Thanks in advance
You could always wrap $http in a service. Since services are only set up one time, you could just have the service factory set up the events for you. It feels a little hackish to me, honestly, but it's a good work around, since Angular doesn't have a global way to do this yet, unless something was added in 1.0.3 that I'm not aware of.
Here's a plunker of it working
And here's the code:
app.factory('httpPreConfig', ['$http', '$rootScope', function($http, $rootScope) {
$http.defaults.transformRequest.push(function (data) {
$rootScope.$broadcast('httpCallStarted');
return data;
});
$http.defaults.transformResponse.push(function(data){
$rootScope.$broadcast('httpCallStopped');
return data;
})
return $http;
}]);
app.controller('MainCtrl', function($scope, httpPreConfig) {
$scope.status = [];
$scope.$on('httpCallStarted', function(e) {
$scope.status.push('started');
});
$scope.$on('httpCallStopped', function(e) {
$scope.status.push('stopped');
});
$scope.sendGet = function (){
httpPreConfig.get('test.json');
};
});
Cagatay is right. Better use $http interceptors:
app.config(function ($httpProvider, $provide) {
$provide.factory('httpInterceptor', function ($q, $rootScope) {
return {
'request': function (config) {
// intercept and change config: e.g. change the URL
// config.url += '?nocache=' + (new Date()).getTime();
// broadcasting 'httpRequest' event
$rootScope.$broadcast('httpRequest', config);
return config || $q.when(config);
},
'response': function (response) {
// we can intercept and change response here...
// broadcasting 'httpResponse' event
$rootScope.$broadcast('httpResponse', response);
return response || $q.when(response);
},
'requestError': function (rejection) {
// broadcasting 'httpRequestError' event
$rootScope.$broadcast('httpRequestError', rejection);
return $q.reject(rejection);
},
'responseError': function (rejection) {
// broadcasting 'httpResponseError' event
$rootScope.$broadcast('httpResponseError', rejection);
return $q.reject(rejection);
}
};
});
$httpProvider.interceptors.push('httpInterceptor');
});
I think interceptors works for versions after 1.1.x.
There was responseInterceptors before that version.
I have verified that this code will work as you expect. As I mentioned above, you are not retrieving the injector that you think you are and need to retrieve the one being used for your app.
discussionApp.config(function($httpProvider) {
$httpProvider.defaults.transformRequest.push(function(data) {
var $injector, $log, $rootScope;
$injector = angular.element('#someid').injector();
$rootScope = $injector.get('$rootScope');
$rootScope.$broadcast('httpCallStarted');
$log = $injector.get('$log');
$log.log('httpCallStarted');
return data;
});
});
The best way to do this is to use an http interceptor. Check this link

Resources