角度模块中的全局通信:事件总线或中介模式/服务

Sup*_*aca 10 javascript design-patterns mediator publish-subscribe angularjs

到目前为止,我已经看到了许多问题的解决方案.最简单的是,当然,对$emit一个事件中$rootScope作为事件总线例如(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');
      });
    }
  }
});
Run Code Online (Sandbox Code Playgroud)

另一个是使用中介或pubsub功能/封闭范围声明服务,例如(在多个控制器和指令之间进行通信.)

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;
  }
);
Run Code Online (Sandbox Code Playgroud)

问题是:

  • 是否有意在角度应用中使用第二个?
  • 每个人相互比较的利弊是什么?

hon*_*n2a 15

在编写AngularJS应用程序时,创建自己的事件发射器实现会产生反作用.Angular已经提供了基于事件的通信所需的所有工具.

  • 使用$emiton $rootScope可以很好地进行全局的服务间通信,并没有任何缺点.
  • 使用$broadcast在自然范围(一个绑定到您的DOM的一部分)提供了作用域视图分量(指令,控制器)之间的通信.
  • 使用$broadcaston $rootScope将前两个点组合在一起(它提供了一个完全全球化的通信平台).这是基本上由任何基于AngularJS的库使用的解决方案.

  • 如果您担心上一个选项中的性能并且您真的想要单独的事件发射器,则可以通过创建隔离的范围($rootScope.$new(true))并$broadcast在其上使用来轻松创建一个.(然后,您可以将其包装到服务中并将其注入任何您想要的位置.)

最后一个选项创建了一个集成到Angular中的完整事件发射器(您的问题中提供的实现至少需要包含所有侦听器调用$apply()以正确集成),如果适合特定用途,可以另外用于数据更改观察-案件.

但是,除非你的应用程序非常庞大,或者你对事件名称冲突真的很偏执,否则前三个选项就足够了.


我不会详细介绍组件之间的其他通信方式.一般来说,当情况需要使用范围,控制器的直接交互或通过DOM节点属性进行通信来进行数据共享时,您应该知道它.


Víť*_*.cz 10

我会说广播是一种Angular方式如何实现这一目标.

但是你的中介可以工作,如果你传递指令的内部功能,例如我在范围内使用了方法,但也可以用控制器方法完成.

我发布时使用了完全相同的工厂.

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);
    }
  };
});
Run Code Online (Sandbox Code Playgroud)

完整示例:http://jsbin.com/mobifuketi/1/edit?html,js,console,output

为了完成,我想补充一点,角度也提供了更多的可能性,指令如何沟通.

需要属性

如果您的指令在层次结构中连接,那么您可以使用require属性来访问其他指令控制器.对于许多情况来说,这是最好的解决方案.

.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");
      };
    }
  };
});
Run Code Online (Sandbox Code Playgroud)

完整示例:http://jsbin.com/turoxikute/1/edit?html,js,console,output

使用$ watch

如果功能依赖于数据而不依赖于操作,那么您将使用$ watch并对存储在共享服务中的给定模型或模型的更改做出反应,它不像监听器,它基本上检查更改.我已将命名方法changeState()和日志"状态已更改"命名为每个人看清楚.

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");
      });
    }
  };
});
Run Code Online (Sandbox Code Playgroud)

完整示例:http://jsbin.com/darefijeto/1/edit?html,js,console,output