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

可紊 提交于 2019-11-27 18:38:07

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.

I would say that broadcasting is an Angular way how to achieve this.

However your mediator can work, if you pass internal funcion of directive, in example I have used method on scope, but it can be done also with controller method.

I have used exact same factory as you post.

angular.module("sharedService", []) 
.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;
  }
)

.directive("directiveA", function(MessageService) {
  return {
    link:function(scope) {
      scope.click = function() {
        MessageService.broadcastMessage("broadcasted message");
      };
    },
    template: '<button ng-click="click()">Click</button>'
  }; 
})
.directive("directiveB", function(MessageService) {
  return {
    link:function(scope) {        
      scope.callback = function(message) {
        console.log(message);
      };

      MessageService.registerListener(scope.callback);
    }
  };
});

Full example: http://jsbin.com/mobifuketi/1/edit?html,js,console,output

Just to be complete, I would like to add, that angular also provides more posibilities how can directives communicate.

Require atribute

If your directives are connected in hierarchy, then you can use require attribute which let you to access other directives controller. This is ussually best solution for many cases.

.directive("directiveA", function() {
  return {
    require: "^directiveB",

    link: function(scope, element, attrs, directiveCtrl) {

      scope.click = function() {
        directiveCtrl.call();
      };
    },
    template: '<button ng-click="click()">Click</button>'
  }; 
})
.directive("directiveB", function() {
  return {
    controller :function() {
       this.call = function() {

        console.log("method has been called");
      };
    }
  };
});

Full example: http://jsbin.com/turoxikute/1/edit?html,js,console,output

Using $watch

If the functionality deppends on data and not on action, you cen use $watch and react on the changes of given model or model stored in shared service , its not like listener, its basicly checking of change. I have named method changeState() and log "state changed" for everybody see it clear.

angular.module("sharedService", []) 
.service("MediatorService", function() {
  this.state = true;

  this.changeState = function() {
     this.state = !this.state;
  };
})

.directive("directiveA", function(MediatorService) {
  return {
    link:function(scope) {

      scope.click = function() {
        MediatorService.changeState();
      };
    },
    template: '<button ng-click="click()">Click</button>'
  }; 
})

.directive("directiveB", function(MediatorService) {
  return {
    link:function(scope) {
        scope.mediator = MediatorService; 
      scope.$watch("mediator.state", function(oldValue, newValue) {
        if (oldValue == newValue) {
          return;
        }  

        console.log("state changed");
      });
    }
  };
});

Full example: http://jsbin.com/darefijeto/1/edit?html,js,console,output

ron pastore

I like an event bus.

Angular does provide $emit on $rootScope but I don't think that should bound your decision to use it for event-based flows if they are complex or foreseeably complex. Angular has lots of features and while most are great, even the authors admit they're mostly meant to compliment good software engineering principles, not replace them.

I like this post on using postal.js: An angular.js event bus with postal.js. The two main benefits are channels and envelopes, which will make for more explicit, understandable and flexible event-based logic.

I find service based approaches to be error prone if state is not managed tightly, which is hard with async calls and injections, where you can't be certain how a service will be multi-purposed in the future.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!