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);
};
}
Related
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.
Snippet 1 does not work. I get Error: [$injector:unpr] Unknown provider: $q
/* Snippet 1*/
var mApp = angular.module('MyApp',[]);
mApp.provider('authProvider', ['$q', function($q) {
this.$get = function(){
authProvider = {};
authProvider.Title = function(){
var deferred = $q.defer();
deferred.resolve("promise resolved");
return deferred.promise;
}
return authProvider;
}
}]);
However, Snippet 2 works. I am confused why that is ? All the factory sample codes i have read, inject the dependency in the first line such as
.factory('MyFactory',[$q,function($q) {}]);
Why doesnt that style work in the provider code above ? Also, why are we injecting $q below in the GET declaration but not further down in the TITLE declaration.
/* Snippet 2*/
mApp.provider('authProvider', function() {
this.$get = function($q){
authProvider = {};
authProvider.Title = function(){
var deferred = $q.defer();
deferred.resolve("promise resolved");
return deferred.promise;
}
return authProvider;
}
});
Please help!!!
(The code doesn't do anything right now. I am just trying to learn syntax)
you can't do direct DI in provider , when you are using provider you have to inject your component in $get .
Reason you cannot inject dependency into the provider directly is that the provider runs during the module loading phase whereas the $get is run when instantiating the service you are providing.
You can not use any service during the loading/configuration phase of your modules.
All the factory sample codes i have read, inject the dependency in the first line such as .factory('MyFactory',[$q,function($q) {}]); Why doesn't that style work in the provider code above ?
This graphic has always helped me understand the difference between a provider and a factory:
source: simplygoodcode.com
The factory function is the $get function of the provider. Before injection, the provider constuction function can configure the $get function. The $get function is where injection happens.
The provider construction function isn't injectable. That's why you get that error. The $get function is injectable and that is the function that you specify with factory. factory is just syntactic sugar for creating a provider with an empty constructor function.
See Also
Confused about Service vs Factory — this answer
Trying to create a global error handler that should present an error modal on error by configuring the $httpProvider adding a interceptor. The modal produces a dependency on a service. Which I can't inject into a config block.
I have tried lazy loading the service using $injector, but doesn't work.
How would you solve it?
edit just found $exceptionHandler, trying it. No luck cirk dep :$modal <- errorModalService <- $exceptionHandler <- $rootScope
Yes, it is true that AngularJS DI subsystem can be tricky with circular dependencies. Not sure what you've tried and what didn't work but you can always get a required dependency from the $injector. Doing so from a $http interceptor is pretty easy:
.factory('errInterceptor', function ($q, $injector) {
return {
responseError: function(response) {
$injector.get('$modal').open({
template: '<h4>$http error!</h4>',
});
}
}
})
Here is a working plunk: http://plnkr.co/edit/n172IrR9259qi4qG0H3I?p=preview
I'm trying to implement a very standard task: when an exception occurs, redirect to my /error page.
In a simplified form the code looks like this:
app.factory('$exceptionHandler', ['$location', function($location) {
return function(exception, cause) {
$location.path("/error");
};
}]);
However, AngularJS complains:
Circular dependency found: $location <- $exceptionHandler <- $rootScope
This looks like a fundamental limitation, not to allow use of $location when handling exceptions.
But how else can we do it then?
To get around this you need to call the $injector manually to resolve the dependency at runtime:
app.factory('$exceptionHandler', ['$injector', function($injector) {
var $location;
return function(exception, cause) {
$location = $location || $injector.get('$location');
$location.path("/error");
};
}]);
Is it possible to do DI in a provider method?
In this example
angular.module('greet',[])
.provider('greeter',function() {
this.$get=function() {
};
})
.service('greeterService',function($http){
console.log($http);
})
;
Injecting $http into service appears to be the correct implementation, but it doesn't work in a provider method and it throws an error:
Unknown provider: $http
Does the provider method work with DI to inject services?
You can certainly inject $http to provider. Just make sure it appears in $get, not the function constructor. As follows:
angular.module('greet',[]).provider('greeter',function() {
this.$get = function($http) {
};
});
You can inject constants and other providers into a provider. Not services or factories - with one exception. It seems that you can inject the $injector service into a provider - at least, you can in AngularJS 1.3.16.
.provider('foo', ['$injector', function ($injector) {
var messagePrefix = $injector.get('msgPrefix');
this.message = '';
this.$get = function() {
var that = this;
return function() {
return messagePrefix + that.message;
}
};
}])
You can use the injector outside the $get method, but you still can't get services from it at configure time.
See here for a demo.
Following up on IgrCndd's answer, here's a pattern that might avoid potential nastiness:
angular.module('greet',[]).provider('greeter', function() {
var $http;
function logIt() {
console.log($http);
}
this.$get = ['$http', function(_$http_) {
$http = _$http_;
return {
logIt: logIt
};
}];
});
Note how similar this is to the equivalent service, making conversion between the two less troublesome:
angular.module('greet',[]).factory('greeter', ['$http', function($http) {
function logIt() {
console.log($http);
}
return {
logIt: logIt
};
});
You actually have to inject the dependency on $get and then store it to use on what you retrieve from $get. Not beautiful at all...
No, you can not inject a service into the provider itself.
Injecting a service into a provider's $get method is the same as injecting a service into a factory, but you can not inject it into the provider function directly.
The difference between $get and the provider itself is that the provider runs during the module loading phase whereas the $get is run when instantiating the service you are providing.
This implies that you can not use any service at all during the module loading/configuration phase of your modules. That is all the stuff you run inside your config blocks, such as when defining your app routes or states, can not make use of any service.
The only other thing you can inject into config blocks besides providers are constants.
You could do something like IgrCndd suggested. But if you needed to consume the provider in a config block, which is the provider's purpose after all, you will not have your values injected until much after. So it's not going to work unless you do some nasty hack using promises.
Further reading on injectables