AngularJS:通过服务在两个示波器/控制器之间进行双向通信

时间:2013-04-26 11:48:01

标签: javascript angularjs

我有很多场景需要点击等来触发页面上其他位置的行为(单向通信场景)。我现在需要双向通信,其中元素A中发生的事物可以修改元素B后面的范围中的特定属性,反之亦然。到目前为止,我一直在使用$rootScope.$broadcast来促进这一点,但感觉有些过度,并且最终在两个地方都创建了样板:

$scope.$on('event-name', function(event, someArg) {
    if(someArg === $scope.someProperty) return;

    $scope.someProperty = someArg;
});

$scope.$watch('someProperty', function(newValue) {
    $rootScope.$broadcast('event-name', newValue);
});

有更好的方法吗?我想通过服务将两个(或三个或N个)范围绑定在一起,但如果没有魔术事件名称和样板,我就没有办法做到这一点。

2 个答案:

答案 0 :(得分:1)

我自己没有用过这个,但this post基本上解释了我会怎么做。 Here's the code说明了这个想法:

(function() {
    var mod = angular.module("App.services", []);

    //register other services here...

    /* pubsub - based on https://github.com/phiggins42/bloody-jquery-plugins/blob/master/pubsub.js*/
    mod.factory('pubsub', function() {
        var cache = {};
        return {
            publish: function(topic, args) { 
                cache[topic] && $.each(cache[topic], function() {
                    this.apply(null, args || []);
                });
            },

            subscribe: function(topic, callback) {
                if(!cache[topic]) {
                    cache[topic] = [];
                }
                cache[topic].push(callback);
                return [topic, callback]; 
            },

            unsubscribe: function(handle) {
                var t = handle[0];
                cache[t] && d.each(cache[t], function(idx){
                    if(this == handle[1]){
                        cache[t].splice(idx, 1);
                    }
                });
            }
        }
    });


    return mod;
})();

如果控制器被“删除”而没有取消订阅,请注意内存泄漏。

答案 1 :(得分:0)

我认为您可以尝试以下服务,



'use strict';
angular.module('test')
  .service('messageBus', function($q) {
    var subscriptions = {};
    var pendingQuestions = [];

    this.subscribe = function(name) {
      subscriptions[name].requestDefer = $q.defer();
      return subscriptions[name].requestDefer.promise; //for outgoing notifications
    }

    this.unsubscribe = function(name) {
      subscriptions[name].requestDefer.resolve();
      subscriptions[name].requestDefer = null;
    }

    function publish(name, data) {
      subscriptions[name].requestDefer.notify(data);
    }

    //name = whom shd answer ?
    //code = what is the question ?
    //details = details abt question.
    this.request = function(name, code, details) {
      var defered = null;
      if (subscriptions[name].requestDefer) {
        if (pendingQuestions[code]) {
          //means this question is already been waiting for answer.
          //hence return the same promise. A promise with multiple handler will get 
          //same data.
          defered = pendingQuestions[code];
        } else {
          defered = $q.defer();
          //this will be resolved by response method.
          pendingQuestions[code] = defered;
          //asking question to relevant controller
          publish(name, {
            code: code,
            details: details
          });
        }
      } else {
        //means that one is not currently in hand shaked with service.
        defered = $q.defer();
        defered.resolve({
          code: "not subscribed"
        });
      }
      return defered.promise;
    }

    //data = code + details
    //responder does not know the destination. This will be handled by the service using 
    //pendingQuestions[] array. or it is preemptive, so decide by code.
    this.response = function(data) {
      var defered = pendingQuestions[data.code];
      if (defered) {
        defered.resolve(data);
      } else {
        //means nobody requested for this.
        handlePreemptiveNotifications(data);
      }
    }

    function handlePreemptiveNotifications() {
      switch (data.code) {
        //handle them case by case
      }
    }
  });




这可以在多控制器通信中用作消息总线。它正在利用promise API的angular notify()回调。所有参与的控制器都应按如下方式订阅服务,



angular.module('test')
  .controller('Controller1', function($scope, messageBus) {
    var name = "controller1";

    function load() {
      var subscriber = messageBus.subscribe(name);
      subscriber.then(null, null, function(data) {
        handleRequestFromService(data);
      });
    }

    function handleRequestFromService(data) {
      //process according to data content
      if (data.code == 1) {
        data.count = 10;
        messageBus.respond(data);
      }
    }

    $scope.$on("$destroy", function(event) {
      //before do any pending updates
      messageBus.unsubscribe(name);
    });

    load();
  });

angular.module('test')
  .controller('Controller2', function($scope, messageBus) {
    var name = "controller2";

    function load() {
      var subscriber = messageBus.subscribe(name);
      subscriber.then(null, null, function(data) {
        handleRequestFromService(data);
      });
    }

    function handleRequestFromService(data) {
      //process according to data content
    }

    $scope.getHorseCount = function() {
      var promise = messageBus.request("controller1", 1, {});
      promise.then(function(data) {
        console.log(data.count);
      });
    }

    $scope.$on("$destroy", function(event) {
      //before do any pending updates
      messageBus.unsubscribe(name);
    });

    load();
  });