Angular error handling on a promise - angularjs

I'm new to Angular and the general idea of promises so please bear with me on this one.
I have this general setup for a promise below:
ApiService.getDataUriForUrl(imageUrl).then(function(url){
requests--;
}, function(error){
console.log('oh no!');
});
I wrote this thinking that the function(error) would catch exceptions thrown on the getDataUriForUrl() call, but I now am led to believe it is for dealing with errors in the then(... area.
How should I be formatting this to intercept errors as I desire? (catching exceptions thrown in the getDataUriForUrl() call).

The errors thrown in the onFulfill callback will not be passed to the onReject callback.
Instead you can pass the first callback to then method and call the then method aging on the return.
ApiService.getDataUriForUrl(imageUrl).then(function(url){
requests--;
return new Error ();// you can't throw it
}).then(null, function(error){
console.log('oh no!');// I can catch error from the getData or the onFulfill callback
});

Instead of calling $q.reject or $q.resolve for every api method you can just write a interceptor for response and responseError. In which you can just $q.reject() or $q.resolve().
This would be cleaner and modularized.
Sample code:
(function (global) {
"use strict";
angular.module("TestAPp").factory('httpInterceptor', ["$rootScope", "$q", function ($rootScope, $q) {
return {
response:function(response){
return response;
},
responseError: function (response) {
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));
You can refer to Error Handling in angular for more info.

Related

Execute code within a factory when needed, not when loaded into controller

Factory:
.factory("myFac", ['$http', '$q', function($http, $q) {
var defer = $q.defer();
$http.get('some/sample/url').then(function (response) { //success
/*
* Do something with response that needs to be complete before
* controller code is executed.
*/
defer.resolve('done');
}, function() { //error
defer.reject();
});
return defer.promise;
}]);
Controller:
.controller("testCtrl", ['$scope', 'myFac', function($scope, myFac) {
/*
* Factory code above is executed immediately as 'myFac' is loaded into controller.
* I do not want this.
*/
if($scope.someArbitraryBool === true) {
//THIS is when I want to execute code within myFac
myFac.then(function () {
//Execute code that is dependent on myFac resolving
});
}
}]);
Please let me know if it is possible to delay the code in the factory until I need it. Also, if there's a better way to execute on this concept, feel free to correct.
You factory has $http.get directly inside factory function which return custom $q promise. So while you inject the factory dependency inside your controller function, it ask angular to create an object of myFac factory function, while creating object of function it executes the code block which you have returned your factory, basically which returns promise.
What you could do is, just return a object {} from the factory function which will have method name with its definition, so when you inject inside angular controller it will return service object, which will various method like getData method. And whenever you want to call the method of factory you could do factoryName.methodName() like myFac.getData().
Also you have created a extra promise inside your service which is not needed in first place, as you can utilize the promise of $http.get (which returns a promise object.)
Factory
.factory("myFac", ['$http', function($http) {
var getData = return $http.get('some/sample/url').then(function (response) { //success
return 'done'; //return to resolve promise with data.
}, function(error) { //error
return 'error'; //return to reject promise with data.
});
return {
getData: getData
};
}]);
Controller
.controller("testCtrl", ['$scope', 'myFac', function($scope, myFac) {
if($scope.someArbitraryBool === true) {
//Here you could call the get data method.
myFac.getData().then(function () {
//Execute code that is dependent on myFac resolving
});
}
}]);

Using $http with $exceptionHandler

I want to post errors that happen inside an angular application.
I followed the approach given in this related question, as suggested in the answer, I injected the $injector and then got the $http service from there.
But the line
Uncaught Error: Circular dependency: $http <- $exceptionHandler <- $rootScope
keeps comming.
here is the fiddle with the problem
with the relevant code:
var mod = angular.module('test', []);
mod.config(function ($provide) {
$provide.decorator("$exceptionHandler", ['$delegate', '$injector', function ($delegate, $injector) {
var $http = $injector.get("$http");
}]);
});
mod.controller('testCtrl', function ($scope) {
});
If you comment the line
var $http = $injector.get("$http");
The circular dependency error is gone.
I think I'm missing something in my understanding. What am I doing wrong? After all, that seems to have worked for others.
Any suggestion on how to achieve my initial goal of 'posting errors to a service' is also welcomed.
Thanks everyone
wrap your injector code within function
var mod = angular.module('test', []);
mod.config(function ($provide) {
$provide.decorator("$exceptionHandler", ['$delegate', '$injector',function ($delegate, $injector) {
return function (exception, cause) {
var $http = $injector.get("$http");
}
}]);
});
mod.controller('testCtrl', function ($scope) {
});
You can do like this.
This will break your circular dependency error and you can achieve your goal too.
If I am not wrong then you just wanna call web service in exception Handler.
var httpCallOnAngularError = angular.module('exceptionTestApp', []);
/*httpCallOnAngularError.factory('$exceptionHandler', function () {
return function (exception, cause) {
alert(exception.message);
};
});*/
httpCallOnAngularError.config(function ($provide) {
$provide.decorator("$exceptionHandler", function ($delegate, $injector) {
return function (exception, cause) {
var $rootScope = $injector.get('$rootScope');
$delegate(exception, cause);
$rootScope.logAngularError();
alert(exception.message);
};
});
});
httpCallOnAngularError.run(function ($http, $rootScope) {
$rootScope.logAngularError = function () {
//Call your webservice here.
$http.get("http://jsonplaceholder.typicode.com/posts/1")
.success(function (response) {
console.log(JSON.stringify(response));
});
};
});
httpCallOnAngularError.controller('exceptionTestCtrl', function ($scope) {
throw {message: 'Log this error by calling webservice.'};
});
You can verify this by this template.
<div ng-app="exceptionTestApp" ng-controller="exceptionTestCtrl">
</div>
Webservice call in Exception Handler $http in $exceptionHandler

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 to do error handling from .config block using httpProvider - 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*/);
}
};
});
});

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.

Resources