$location from $exceptionHandler - dependency conflict - angularjs

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");
};
}]);

Related

Angular circular reference when using $http to log exceptions

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.

AngularJS - unknown provider $modal when getting a service from angular.injector

I have a piece of javascript code which needs to call an angular service. I try to access the service by retrieving the angular module where the service is defined and then getting the service it self:
var messagemanagerModule = angular.injector(['ng', 'portal.services.messagemanager']);
var messageService = messagemanagerModule.get('MessageService');
messageService.postMessage('portal', moduleId, 'selectModule');
The module and service is defined like this:
angular.module('portal.services.messagemanager', ['modules.modal', 'modules.softlogoff'])
.factory('MessageService', messageService);
messageService.$inject = ['$rootScope', '$modal', '$translate', 'ConfigurationService'];
function messageService($rootScope, $modal, $translate, ConfigurationService) {
var service = {
showMessage: showMessage,
showSoftLogoff: showSoftLogoff,
postMessage: postMessage,
supportedMessages: supportedMessages
};
return service;
Unfortunately I get the error:
Error: $injector:unpr Unknown provider: $modalProvider <- $modal <- MessageService"
I think I need to inject $modal, but I don't know how to do it.
The ui-bootstrap library now uses `$uibModal' service.
So you need to inject $uibModal service.
messageService.$inject = ['$rootScope', '$uibModal', '$translate', 'ConfigurationService'];
function messageService($rootScope, $uibModal , $translate, ConfigurationService) {
var service = {
showMessage: showMessage,
showSoftLogoff: showSoftLogoff,
postMessage: postMessage,
supportedMessages: supportedMessages
};
return service;
I think you need to include the ui.bootstrap module as a dependency of your module:
include ui.bootstrap in you dependencies:
angular.module('portal.services.messagemanager', ['modules.modal','modules.softlogoff','ui.bootstrap'])
Further you need to include the ui-bootstrap-xxx.min.js somewhere in your html.
If you use ui-bootstrap in version >= 0.14 you need to change $modal to $uibModal

provider not found for underscore

i have an angularjs factory to which i am injecting underscore and application is working fine but when i try to write jasmine test cases on it i am getting an error underscore provider is not found
i have my factory like
angular.module("sample")
.factory("example", example);
example.$inject = ["$document", "$compile", "$rootScope", "$timeout", "$q", "underscore"];
function example($document, $compile, $rootScope, $timeout, $q, _) {
}
and i have my module defined as
(function(){
angular.module(samlple,[]);
})();
and my test case is as
beforeEach(module('sample'));
beforeEach(module('ionic'));
beforeEach(inject(function ($document, $compile, $rootScope, $timeout,underscore,example) {
}
its giving error
Error: [$injector:unpr] Unknown provider: underscoreProvider <- underscore
Add import underscore in your index.html, then add it as a service.
var underscore = angular.module('underscore', []);
underscore.factory('_', function() {
return window._; // assumes underscore has already been loaded on the page
});
And
//Now we can inject underscoreJS in the controllers
function MainCtrl($scope, _) {
//using underscoreJS method
_.max([1,2,3,4]); //It will return 4, which is the maximum value in the array
}
But I recommend you to use lodash! It has more cool functions. Information about how to use lodash with Angular you can find here .
Piggybacking on #Bakhtier's answer, I used the following to get Karma/Jasmine to recognize lodash so I could use it in my services, as well as the rest of my app.
angular.module('app', ['app.services', 'lodash']);
angular.module('app.services', ['lodash']).factory('MyService', ['_', function (_){
// your code bits
}]);
angular.module('lodash', []).factory('_', function() {
return window._;
});
Hope that helps someone out.

Angularjs: check if debug has been enabled in $log decorator

I created a decorator for $log in Angular. The $logProvider has the function debugEnabled, but from what I understand I only have access to the $log service. Is it possible to access the function 'debugEnabled()' of the $logProvider in a decorator somehow?
$provide.decorator( '$log', [ "$delegate", function( $delegate ) {
// Saving the original
var _$log = (function( $delegate ) {
return {
log : $delegate.log,
info : $delegate.info,
warn : $delegate.warn,
debug : $delegate.debug,
error : $delegate.error
};
})( $delegate ),
}]);
debugEnabled() is a method on the $logProvider itself. Remember that $logProvider is what is responsible for generating the $log service (via its $get function).
What decorators do is intercept the object that is returned from $get and allow you to add methods etc.
So to modify decorator behaviour based on whether debug is enabled, you simply need to inject $logProvider into your .config block and your decorator will have a closure over it:
angular.module('example', [])
.config( function ($provide, $logProvider){
$provide.decorator('$log', function ( $delegate ){
if ($logProvider.debugEnabled())
// do something
else
// do something else
return $delegate;
}
})

Injecting $http into angular factory($exceptionHandler) results in a Circular dependency

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);
};
}

Resources