I'm overriding the Angular $exceptionHandler to do some custom logging of exceptions. Unfortunately, my Logging service uses $http for logging, which is dependent on $exceptionHandler. Any thoughts on a pattern that would resolve my circular reference and still allow me to log via $http?
Here is my Service overriding $exceptionHandler:
angular.module('dashboard').factory('$exceptionHandler', ['$log', 'Logging',
function($log, Logging){
return function globalErrorHandler(exception, cause){
var itemToLog = new logItem('dashboard', 'General Error', exception.message + ": " + exception.stack);
Logging.logEvent(itemToLog);
$log.warn(exception, cause);
}
}]);
and my custom Logging service:
angular.module('dashboard').factory('Logging', ['$http', function($http) {
return {
logEvent: function(item){
$http.post('/api/loggingservice/event', item)
.success(function(data){
return data;
}).
error(function(data){
console.log('error logging event: '+JSON.stringify(data));
});;
}
};
}]);
And the error message I receive is:
angular.min.js:6 Uncaught Error: [$injector:cdep]
https://docs.angularjs.org/error/$injector/cdep?p0=$http%20%3C-%20Logging%20%3C-%20$exceptionHandler%20%3C-%20$rootScope%20%3C-%20$http%20%3C-%20UserManagement%20%3C-%20Menus
$exceptionHandler is used by other core services that $http depends on. This makes injecting $http or a service that depends on $http impossible, because this results in circular dependency.
A usual recipe to avoid CD in Angular 1 is using $injector.get(...) instead of injecting a service in service factory/constructor function. However, the developer should be aware why it is done and what it is going on there.
Doing something like
function($log, $injector){
var Logging = $injector.get('Logging');
return function globalErrorHandler(exception, cause){ ... }
}
won't break circular dependency, because $exceptionHandler is eagerly instantiated by core services which $http depends on, which Logging depends on.
On the other hand,
function($log, $injector){
return function globalErrorHandler(exception, cause){
var Logging = $injector.get('Logging');
...
}
}
will work because this way Logging is lazily instantiated. This will result in executing $injector.get(...) on each handler call. But this is fine, since $injector.get(...) has no performance impact and can be called multiple times, especially in non-critical places.
Related
Let's suppose I have a simple service:
angular.module('myModule')
.factory('myService', ['$http', function($http) {
return $http.get('/something');
}]);
Now whenever I inject the service into a controller, the router waits for the promise to resolve before changing the route, which is exactly what I want.
However, there doesn't seem to then be a way to return other data from the service. What if the service needs to also provide some methods? How can I do this while still maintaining the dependency behavior described above?
You can use the Angular implementation to promise pattern $q to create a promise which will be resolved when the $http.get gets resolved, and resolve the main one with the data coming from your HTTP resource and other data and functions:
angular.module('myModule')
.factory('myService', ['$http', '$q',
function($http, $q) {
var deferred = $q.defer();
$http.get('/something').then(function(data) {
deferred.resolve({
data: data,
doStuff: function() {}
});
});
return deferred.promise;
}
]);
BTW, I'm not sure if the result of some HTTP request is exactly a service at all. It seems like the whole GET should be encapsulated by a function of your myService service, and call it in some controller, service, directive or wherever you need to call it, and provide a continuation with .then there instead.
Or, if you're using UI Router, you might be able to call a service function which returns a promise form a route resolver and it will do the job while implementing your services in the right way.
I just start learning AngularJS. I don't know where to find a tutorial to arm myself with a knowledge of dealing an exception in the following code:
angular.module('myApp').controller('MyController', ['$scope', '$stateParams', '$modalInstance', 'entity', 'MyEntity',
function($scope, $stateParams, $modalInstance, entity, MyEntity) {
...
var onSaveFinished = function (result) {
$scope.$emit('myApp:myEntityUpdate', result);
$modalInstance.close(result);
};
$scope.save = function () {
MyEntity.save($scope.myEntity, onSaveFinished); <-- an exception can occur on this line
I need to catch any exception on an Ajax call and display an error accordingly on the html file. I have tried using .catch and .error without a luck of catching an exception.
Can someone point a direction on where to learn a knowledge on this topic.
Try to use $exceptionHandler service. That's what Angular's docs say:
Any uncaught exception in angular expressions is delegated to this service. The default implementation simply delegates to $log.error which logs it into the browser console.
To add your own exception handling, you should override the normal action of $exceptionHandler (example in docs).
I've been struggling with this all morning. I'm trying to call a Service from within my config($httpprovider). I've read a lot of people explaining that Services are not yet available during config. I get that, but my service should only be called from within the interceptor, which is at runtime.
I found a semi-solution that manually injected the Service manually like below, but it's not working for me, since it seems a completely new instance of the Service is being created, and I want to keep using the same instance throughout the app (since it stores a messageQueue array). Any suggestions?
.config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push(function ($q) {
return {
'responseError': function (rejection) {
//responses 300 and up are errors
//Manually inject service from the myServices module, since it is not yet known when .config is called
var $injector = window.angular.injector(['myServices']);
var MessageService = $injector.get('MessageService');
MessageService.setMessage("We were unable to load all necessary data. We're terribly sorry! Please try reloading this page or contact us if the problem isn't solved.");
}
};
});
}])
You can register your interceptor as a service using a factory, according to angularjs 1.2.2 documentation, so it should be provided with the right dependencies, using the following syntax :
.config(['$httpProvider', function ($httpProvider) {
$provide.factory('myHttpInterceptor', ['MessageService', function (MessageService) {
return {
'responseError': function (rejection) {
//responses 300 and up are errors
MessageService.setMessage("We were unable to load all necessary data. We're terribly sorry! Please try reloading this page or contact us if the problem isn't solved.");
}
};
}]);
$httpProvider.interceptors.push('myHttpInterceptor');
}]);
When I try inject $http into an overridden factory I get the error:
Uncaught Error: [$injector:cdep] Circular dependency found: $http <-
$exceptionHandler <- $rootScope
AngularModule.factory('$exceptionHandler', function ($http) {
any ideas how to resolve? if I inject using [], $http is undefined
edit__________________
as per an answer below I tried:
MyModule.config(function($provide, $http) {
$provide.decorator("$exceptionHandler", function($delegate) {
return function(exception, cause) {..
but I still get the circular error:
Uncaught Error: [$injector:cdep] Circular dependency found: $http <-
$exceptionHandler <- $rootScope
Inject the $injector and then get the $http service from there. Something like this:
AngularModule.factory('$exceptionHandler', function ($injector) {
var $http = $injector.get("$http");
See https://groups.google.com/forum/#!topic/angular/lbFY_14ZtnU/discussion
However, this will completely override the $exceptionHandler functionality provided by Angular. If you just want to add the server-side log to the existing functionality, see this question about augmenting $exceptionHandler functionality.
I'm using this solution, because of circular dependency issues with rootScope:
angular
.module('facilityLog')
.provider('$exceptionHandler', function() {
"use strict";
this.$get = function($injector) {
function exceptionHandler(exception, cause) {
// This is the part where you get the instance of $http in your case
var $rootScope = $injector.get('$rootScope');
//...
}
return exceptionHandler;
}});
So if you request the instance inside the exceptionHandler-Function you will not get the circular dependency-error.
I used the following to solve this. Note how the array notation is used to make this minification safe.
Note also, that I am completely overriding the $esceptionHandler and using my own service to replace it.
angular
.module('app')
.factory('$exceptionHandler', $exceptionHandler);
$exceptionHandler.$inject = ['$injector', 'exceptionLoggingService'];
function $exceptionHandler($injector, exceptionLoggingService)
{
return function(exception, cause)
{
exceptionLoggingService.http = exceptionLoggingService.http || $injector.get('$http');
exceptionLoggingService.error(exception, cause);
};
}
New to AngularJS but slowly getting going with it and so far I like it. I'm coming from the Java/JSP world so bit of a learning curve!
Anyway, I'm trying to figure out how to send all logging to a server side service.
In my app module config I've overridden the Log implementation and I have this working fine - I have log statements automatically creating simply alerts.
Next step was to send them to the server. I've create a service for this using $resource. I then try to autowire the service into my app module config and this is where I've problems.
It's giving me a circular dependency error which I'm not sure what it means or how to resolve it.
Anyone done anything similar before who may have encountered this problem?
The error I'm seeing is 'Uncaught Error: Circular dependency: $browser <- $httpBackend <- $http <- $resource <- LoggingService <- $log <- $exceptionHandler <- $rootScope'
My app config is:
app.config(['$provide', function($provide) {
$provide.decorator('$log', function($delegate, LoggingService) {
var _info = $delegate.info;
var _error = $delegate.error;
$delegate.info = function(msg){
_info(msg);
};
$delegate.error = function(msg){
_error(msg);
//log.error(msg);
alert('Error:' + msg);
};
return $delegate;
});
}]);
Just trying to pass in my LoggingService results in the error.
My logging service is very simple:
app.factory('LoggingService', ['$resource', function($resource) {
return $resource('http://localhost:port/myservice/logging/', {port: ':8080'},
{
save: {method: 'POST'}
}
);
}]);
Regards,
Kevin.
As per Injecting Dependencies in config() modules - AngularJS
You can only use providers in .config
See registering a service with $provide
https://docs.angularjs.org/guide/services