global communication in angular module: event bus or mediator pattern/service

Super Babaca picture Super Babaca · Dec 8, 2014 · Viewed 10.3k times · Source

So far I have seen many solutions of the problem. The simplest one is, of course, to $emit an event in $rootScope as an event bus e.g. ( https://github.com/btilford/anti-patterns/blob/master/angular/Angular.md )

angular.module('myModule').directive('directiveA', function($rootScope) {
  return {
    link : function($scope, $element) {
      $element.on('click', function(event) {
        $rootScope.$emit('directiveA:clicked', event);
      });
    }
  }
});
angular.module('myModule').directive('directiveB', function() {
  return {
    link : function($scope, $element) {
      $rootScope.on('directiveA:clicked', function(event) {
        console.log('received click event from directiveA');
      });
    }
  }
});

and another one is to declare a service with a mediator or pubsub functionality / an enclosed scope e.g. ( Communicating between a Multiple Controllers and a directive. )

module.factory('MessageService',
  function() {
    var MessageService = {};

    var listeners = {};
    var count = 0;
    MessageService.registerListener = function(listener) {
      listeners[count] = listener;
      count++;

      return (function(currentCount) {
        return function() {
          delete listeners[currentCount];
        }
      })(count);
    }

    MessageService.broadcastMessage = function(message) {
      var keys = Object.keys(listeners);

      for (var i = 0; i < keys.length; i++) {
        listeners[keys[i]](message);
      }
    }

    return MessageService;
  }
);

The question are:

  • is there point to use the second one in an angular application?
  • and what are pros and cons of each of those in comparison to each other?

Answer

hon2a picture hon2a · Dec 15, 2014

Creating your own implementation of event emitter is counter-productive when writing an AngularJS application. Angular already provides all tools needed for event-based communication.

  • Using $emit on $rootScope works nicely for global inter-service communication and doesn't really have any drawbacks.
  • Using $broadcast on a natural scope (one that is bound to a part of your DOM) provides scoped communication between view components (directives, controllers).
  • Using $broadcast on $rootScope brings the two previous points together (it provides a completely global communication platform). This is the solution used basically by any AngularJS-based library out there.

and

  • If you're worried about performance in the previous option and you really want your separate event emitter, you can easily create one by creating an isolated scope ($rootScope.$new(true)) and using $broadcast on it. (You can then wrap it into a service and inject it anywhere you want.)

The last option creates a full-fledged event emitter integrated into Angular (the implementation provided in your question would at least need to wrap all listener calls in $apply() to integrate properly) that can be additionally used for data change observation, if that fits a particular use-case.

However, unless your application is really humongous, or you're really paranoid about event name collisions, the first three options should suffice just fine.


I won't go into detail about other means of communication between your components. Generally speaking, when the situation calls for data sharing using scope, direct interaction of controllers, or communication through DOM Node attributes, you should know it.