0

I've created a factory to provide my own implementation of Angular's $exceptionHandler. This factory emits an event on $rootScope to signal listeners that an error has occurred. My listener is defined in a directive which is watching for the $rootScope.$on event. Everything seems fine insofar as my factory instance intercepts the exception and emits the error event. However, the directive which is supposedly listening for the event, never receives said event. What is the disconnect that is causing this event to be missed by the listener in the directive??

Clarification: The eventing relies on emitting and consuming entirely on $rootscope so as to avoid broadcasting events downstream. This is by design and has previously worked, or at least appears to work in an httpLoader and an httpErrorHandler that I wrote based off of several implementations which used the same strategy. http://plnkr.co/edit/N0JwYJdxPLur4xYZTUP1?p=info

Plunker: http://plnkr.co/edit/LDbRa0Ew255fOd0EunAq?p=preview

angular.module('demo.services',[]);
angular.module('demo.directives',[]);
angular.module('demo',['demo.services','demo.directives']);

angular.module('demo')
.controller('demoCtrl', function($scope){
  $scope.message="Generate Error";
  $scope.generateError =function(){
    var x;
    x.foo.oops = ':(';
  }
});

angular.module('demo.services')
.factory('$exceptionHandler', function () {
    var $injector = angular.injector(['ng']);
    return function errorCatcherHandler(exception, cause) {
      rootScope = $injector.get('$rootScope');
      rootScope.$emit("httpError",exception);
      console.error(exception);
    };
});


angular.module('demo.directives')
.directive('ngHttpError', ['$rootScope',
  function ($rootScope) {
    return {
      scope: {
        title: '@',
      },
      template: '<div ng-click="showError=false"' +
                'ng-show="showError">'+
                'ERROR! {{errorModel | json}}' + 
                '</div>',
      link: function ($scope) {
        var showError;

        $scope.showError = false;
        $scope.errorModel = {};
        showError = $scope.showError;

        var initScope = function(e,m){
          console.log(m);
          $scope.errorModel = m;
          $scope.showError=true;
        }
        $rootScope.$on("httpError", initScope);
      }
    };
  }
]);

angular.bootstrap(document,['demo']);
mccainz
  • 3,478
  • 5
  • 35
  • 45

1 Answers1

2

Remember that $emit() sends the event upwards in the scope hierarchy and $broadcast() sends the event down the hierarchy. Since $rootScope is the "root" scope (the topmost level/node), it would make sense to use $broadcast() here to send the event "downwards" to child scopes.

$emit(), from the docs:

Dispatches an event name upwards through the scope hierarchy notifying the registered $rootScope.Scope listeners.

$broadcast(), from the docs:

Dispatches an event name downwards to all child scopes (and their children) notifying the registered $rootScope.Scope listeners.

-- Update:

I have updated your $exceptionHandler to get injected the $injector service and to do a $broadcast() instead of an $emit() - that seems to do the trick:

angular.module('demo.services')
.factory('$exceptionHandler', function ($injector) {
    //var $injector = angular.injector(['ng']);
    return function errorCatcherHandler(exception, cause) {
      rootScope = $injector.get('$rootScope');
      rootScope.$broadcast("httpError",exception.message);
      console.error(exception);
    };
});

Btw, a related question / answer can be seen here: AngularJs/ .provider / how to get the rootScope to make a broadcast?

Community
  • 1
  • 1
Lasse Christiansen
  • 10,205
  • 7
  • 50
  • 79
  • Only that I dont wish to broadcast the event down to child scopes. This contains the messaging strategy entirely to $rootscope which is the intent. See: http://plnkr.co/edit/N0JwYJdxPLur4xYZTUP1?p=preview – mccainz Oct 02 '14 at 18:19
  • btw: did you test that this was the issue and confirmed it? – mccainz Oct 02 '14 at 18:31
  • well, I have tried $broadcasting from your `generateError()` function - and that works while emitting does not (as expected based on what I write in my answer). Right now, I'm trying to figure out why broadcasting from your exception handler does not work :) – Lasse Christiansen Oct 02 '14 at 18:33
  • Got it working @mccainz - I have updated my answer with a working code snippet. – Lasse Christiansen Oct 02 '14 at 18:56
  • Dependency injecting $injector did the trick. $emit now works as intended ($broadcst versus $emit was not the breaking issue). What I am curious about is what exactly was the difference between the $rootScopes caused by using ng->Injector versus $injector as a dependency. Thank you so much for your help! – mccainz Oct 02 '14 at 19:19
  • Yeah, I'm wondering about that as well - couldn't find any good explanation for it though. Glad I could help :) – Lasse Christiansen Oct 02 '14 at 19:21