Reacting on angular events in a directive without injecting $rootScope

16,079

Solution 1

If you are only listening for events, you don't have to use the $rootScope; do e.g. $scope.$on("$routeChangeError") on the scope of the directive, from either controller or link function.

You see the "$routeChangeError" is broadcasted from the $rootScope, so all children receive it.

Solution 2

No. There isn't any way to listen to Angular events without having some sort of access to $scope. That is one of the main reasons they have the $rootScope service, so that you can gain access to $scope while in a service or some other module.

However, in a directive, you don't need to inject $rootScope because you already have an isolate scope in the directive in the linking function.

For example:

.directive('myDirective', function(){
    return {
        restrict: 'A',
        link: function(scope, elem, attrs){
            scope.$on('some-event', function(e){
                // respond to event here
            });
        }
    };
});

Your scope variable is an "isolate" scope so it cannot affect its parent scope in any way. This does however have some strange side effects if you're trying to emit an event out to some sister-level module, because Angular events only $emit upwards or $broadcast downwards through the scope hierarchy. If you are not, then the above example should work perfectly. If you are, then the following example is one way to do it:

.directive('myDirective', function($rootScope){
    var isolateScope = $rootScope.new();  // creates a new isolate copy of $rootScope
    return {
        restrict: 'A',
        link: function(scope, elem, attrs){
            isolateScope.$on('some-event', function(e){
                // respond to event here
            });
        }
    };
});

This will create a similar isolate copy of $rootScope so that any changes you make to it will not affect other modules or services. This will prevent any issues with "smell-code" as you called it.

Solution 3

$on method is defined on internal Scope.prototype, and since each scope in Angular is created from Scope constructor this means each scope has a $on method available on itself. There's no need to inject $rootScope, just call the $on method on directive linking function's scope directly:

app.directive('myDirective', function(){
  return function(scope, element, attrs){
    scope.$on('$routeChangeError', function(){
      // ...
    });
  }
});
Share:
16,079
Adam Bogdan Boczek
Author by

Adam Bogdan Boczek

Updated on June 14, 2022

Comments

  • Adam Bogdan Boczek
    Adam Bogdan Boczek almost 2 years

    I wonder if you have an example of a directive code that reacts on angular events like $routeChangeError without injecting $rootScope in to it (to use $on in link function). It breaks in my opinion MV* pattern and "produces" smell-code (gives the possibility to manipulate root scope in a directive). Thanks in advance.

  • Adam Bogdan Boczek
    Adam Bogdan Boczek over 10 years
    Does it mean that the solution shown by John Lindquist in his tutorial egghead.io/lessons/angularjs-directive-for-route-handling is simply bad designed (Use AngularJS $rootScope within a directive to detect route change errors and display it to the user)? Actually this video was the trigger of my question.