How to override $exceptionHandler implementation
Asked Answered
F

3

73

There are some extra things we want to do anytime a javascript exception is thrown.

From the docs on $exceptionHandler:

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.

The fact that it says "default implementation" makes me think there's a way we can provide our own implementation for the service, and do the stuff we want when an exception is thrown. My question is, how do you do this? How can we keep all exceptions going to this service, but then provide functionality we want to happen?

Footy answered 28/11, 2012 at 0:2 Comment(1)
Please select the '$provide.decorator' solution as the answer to this question.Peshawar
F
173

Another option I've found for this is to "decorate" the $exceptionHandler through the $provide.decorator function. This provides you with a reference to the original implementation if you want to use it as part of your custom implementation. So, you can do something like this:

mod.config(function($provide) {
    $provide.decorator("$exceptionHandler", ['$delegate', function($delegate) {
        return function(exception, cause) {
            $delegate(exception, cause);
            alert(exception.message);
        };
    }]);
});

It will do what the original exception handler does, plus custom functionality.

See this updated fiddle.

Footy answered 5/6, 2013 at 14:46 Comment(5)
+1 on this - it's the better way to handle the problem for sure.Palmerpalmerston
Used this but it doesn't seem to do minification properly!Giselegisella
Does it work with using the array notation like you did in your edit? You have to do that here just like anywhere else dependency injection is used to work with minification.Footy
@Footy yes it does, I just applied the edit after I figured it out.Giselegisella
I would advice CAUTION: This can swallow all exceptions in your karma unit tests, which definately don't want to happen.Vlada
E
56

You can override the $exceptionHandler functionality by creating a service with the same name:

var mod = angular.module('testApp', []);

mod.factory('$exceptionHandler', function () {
    return function (exception, cause) {
        alert(exception.message);
    };
});

See this fiddle for a sample. If you comment out the factory definition for $exceptionHandler you will see the error will log to the console instead of alerting it.

Here is a group thread that has an example of injecting other services like $http using $injector.

Note: if you don't want to overwrite the existing functionality of the $exceptionHandler (or another built in service) see this answer for information on how to decorate a service.

Ekaterinburg answered 28/11, 2012 at 0:24 Comment(7)
So, basically you can override any service and the last definition wins, right?Footy
It looks that way though I've only ever tried with $exceptionHandlerEkaterinburg
I very much prefer @dnc253's solution over this one. By using the "decorator" functionality, you get to preserve the existing behavior AND allow other decorators to get in on the action. This one will clobber anything already there, including your own code if you've already done this elsewhere for a different reason.Renaerenaissance
I agree completely the decorator is a cleaner way to do this.Ekaterinburg
@Ekaterinburg well im enjoying a nice chicken and egg problem with dependancy injection in angular, see if you can have a look: #22684456Patriciate
Although decorators seem better, this appears to be a great solution if you want to use a factory or anything else not initialized at the time of module.configInmost
Does this work if you configure it in a separate, included module, provided it's the last definition?Slaw
D
-5

You can override any service/factory even $cookieStore.If you want a full-blown, configurable object here is a really nice example:

var myApp = angular.module('myApp', []);

//provider style, full blown, configurable version     
myApp.provider('helloWorld', function() {

    this.name = 'Default';

    this.$get = function() {
        var name = this.name;
        return {
            sayHello: function() {
                return "Hello, " + name + "!"
            }
        }
    };

    this.setName = function(name) {
        this.name = name;
    };
});

//hey, we can configure a provider!            
myApp.config(function(helloWorldProvider){
    helloWorldProvider.setName('World');
});


function MyCtrl($scope, helloWorld, helloWorldFromFactory, helloWorldFromService) {

   helloWorld.sayHello(),
}
Dachshund answered 13/7, 2013 at 15:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.