【问题标题】:angular JS - communicate between non-dependend servicesangularJS - 非依赖服务之间的通信
【发布时间】:2013-03-10 13:53:14
【问题描述】:

我是 Angular 新手,遇到了一个 catch-22:

事实:

  1. 我有一项服务可以记录我的资料 (my-logger)。

  2. 我已经用我自己的实现替换了(角度的)$ExceptionHandler,它将未捕获的异常转发到 my-logger 服务

  3. 我有另一个服务 pusher-service,每当要使用“my-logger”在我的应用程序某处记录致命消息时都需要通知该服务。

问题:

我不能让 'my-logger' 依赖于 'pusher',因为它会创建循环依赖(因为 'pusher' 使用 $http。圆圈:$ExceptionHandler -> my-logger -> pusher -> $ http -> $ExceptionHandler...)

我的尝试:

为了使这两个服务相互通信,我想在 pusher-service 上使用 $watch:监视 $rootscope 上的一个属性,该属性将在 my-logger 中更新。 但是,当尝试在“my-logger”中使用 $rootScope 时,为了更新“推送器”“监视”的属性,我在循环依赖方面失败了,因为事实证明 $rootscope 依赖于 $ExceptionHandler(圆圈: $ExceptionHandler -> my-logger -> $rootScope -> $ExceptionHandler)。

试图找到一个选项来在运行时获取在其上下文“my-logger”服务中工作的范围对象。找不到这样的选项。

也不能使用广播,因为它需要 my-logger 才能访问范围 ($rootScope),而如上所示,这是不可能的。

我的问题:

是否有一种角度方式让两个服务通过第 3 方实体进行通信?

知道如何解决这个问题吗?

【问题讨论】:

    标签: angularjs


    【解决方案1】:

    使用作为通知/发布订阅服务的第三个服务:

    .factory('NotificationService', [function() {
        var event1ServiceHandlers = [];
        return {
            // publish
            event1Happened: function(some_data) {
                angular.forEach(event1ServiceHandlers, function(handler) {
                    handler(some_data);
                });
            },
            // subscribe
            onEvent1: function(handler) {
                event1ServiceHandlers.push(handler);
            }
        };
    }])
    

    上面,我只展示了一种事件/消息类型。每个额外的事件/消息都需要自己的数组、发布方法和订阅方法。

    .factory('Service1', ['NotificationService',
    function(NotificationService) {
        // event1 handler
        var event1Happened = function(some_data) {
            console.log('S1', some_data);
            // do something here
        }
        // subscribe to event1
        NotificationService.onEvent1(event1Happened);
        return {
            someMethod: function() {
               ...
               // publish event1
               NotificationService.event1Happened(my_data);
            },
        };
    }])
    

    Service2 的编码与 Service1 类似。

    请注意 $rootScope、$broadcast 和范围如何不用于此方法,因为服务间通信不需要它们。

    通过上述实现,服务(一旦创建)在应用的生命周期内保持订阅状态。您可以添加方法来处理退订。

    在我当前的项目中,我使用相同的 NotificationService 来处理控制器范围的 pubsub。 (如果有兴趣,请参阅Updating "time ago" values in Angularjs and Momentjs)。

    【讨论】:

    • 如果可能的话,我们将不胜感激。我无法完全理解这一点
    • 我已经创建了一个关于这个的问题。 stackoverflow.com/questions/35293132/…有时间请看一下。基本上,我希望看到一个有效的 plunker 示例,以便更好地理解它。干杯
    【解决方案2】:

    是的,使用事件和监听器。

    在您的“我的记录器”中,您可以在捕获新日志时广播事件:

    $rootScope.$broadcast('new_log', log); // where log is an object containing information about the error.
    

    然后在“推动者”中监听该事件:

    $rootScope.$on('new_log', function(event, log) {... //
    

    这样你就不需要任何依赖了。

    【讨论】:

    • 不,我不能。 'my-logger' 不能单独使用 $rootScope,因为 $rootScope 依赖于 $ExceptionHandler,这将导致循环依赖($ExceptionHandler -> 'my-logger' -> $rootScope -> $ExceptionHandler)。如原始消息所述,无法使用范围的任何功能:广播、发射、观看......因为“my-logger”由于循环依赖而无法访问它。
    【解决方案3】:

    我已部分成功解决此案: 我使用 $injector 创建了 'my-logger' 和 'pusher' 之间的依赖关系。 我在 'my-logger' 中使用了 $injector 并在致命消息到达时在“运行时”(意味着在即将使用它而不是在服务声明时)注入推送服务。 仅当我在发送发生之前在“运行时”将 $http 注入到“推送器”时才有效。

    我的问题是为什么它在“运行时”中与注入器一起工作,而不是与服务头部声明的依赖项一起工作?

    我只有一个猜测: 这是时间问题: 当服务在“运行时”注入时,如果它已经存在(意味着已经在其他地方初始化),那么 不需要 获取并获取其所有依赖项,因此永远不会发现圈子 并且永远不会停止执行。

    我说的对吗?

    【讨论】:

      【解决方案4】:

      这是在服务和控制器之间发布/订阅多个事件的简单方法

      .factory('$eventQueue', [function() {
        var listeners = [];
        return {
          // publish
          send: function(event_name, event_data) {
              angular.forEach(listeners, function(handler) {
                if (handler['event_name'] === event_name) {
                  handler['callback'](event_data);
                }                
              });
          },
          // subscribe
          onEvent: function(event_name,handler) {
            listeners.push({'event_name': event_name, 'callback': handler});
          }
        };
      }])
      

      消费者和生产者

      .service('myService', [ '$eventQueue', function($eventQueue) {
        return {
      
          produce: function(somedata) {
           $eventQueue.send('any string you like',data);
          }
      
        }
      }])
      
      .controller('myController', [ '$eventQueue', function($eventQueue) {
        $eventQueue.onEvent('any string you like',function(data) {
          console.log('got data event with', data);
      }])
      
      .service('meToo', [ '$eventQueue', function($eventQueue) {
        $eventQueue.onEvent('any string you like',function(data) {
          console.log('I also got data event with', data);
      }])
      

      【讨论】:

        【解决方案5】:

        您可以制作自己的通用事件发布服务,并将其注入到每个服务中。

        这是一个例子(我没有测试过,但你明白了):

                .provider('myPublisher', function myPublisher($windowProvider) {
                    var listeners = {},
                        $window = $windowProvider.$get(),
                        self = this;
        
                    function fire(eventNames) {
                        var args = Array.prototype.slice.call(arguments, 1);
        
                        if(!angular.isString(eventNames)) {
                            throw new Error('myPublisher.on(): argument one must be a string.');
                        }
        
                        eventNames = eventNames.split(/ +/);
                        eventNames = eventNames.filter(function(v) {
                            return !!v;
                        });
        
                        angular.forEach(eventNames, function(eventName) {
                            var eventListeners = listeners[eventName];
        
                            if(eventListeners && eventListeners.length) {
                                angular.forEach(eventListeners, function(listener) {
                                    $window.setTimeout(function() {
                                        listener.apply(listener, args);
                                    }, 1);
                                });
                            }
                        });
        
                        return self;
                    }
                    function on(eventNames, handler) {
                        if(!angular.isString(eventNames)) {
                            throw new Error('myPublisher.on(): argument one must be a string.');
                        }
        
                        if(!angular.isFunction(handler)) {
                            throw new Error('myPublisher.on(): argument two must be a function.');
                        }
        
                        eventNames = eventNames.split(/ +/);
                        eventNames = eventNames.filter(function(v) {
                            return !!v;
                        });
        
                        angular.forEach(eventNames, function(eventName) {
                            if(listeners[eventName]) {
                                listeners[eventName].push(handler);
                            }
                            else {
                                listeners[eventName] = [handler];
                            }
                        });
        
                        return self;
                    }
                    function off(eventNames, handler) {
                        if(!angular.isString(eventNames)) {
                            throw new Error('myPublisher.off(): argument one must be a string.');
                        }
        
                        if(!angular.isFunction(handler)) {
                            throw new Error('myPublisher.off(): argument two must be a function.');
                        }
        
                        eventNames = eventNames.split(/ +/);
                        eventNames = eventNames.filter(function(v) {
                            return !!v;
                        });
        
                        angular.forEach(eventNames, function(eventName) {
                            if(listeners[eventName]) {
                                var index = listeners[eventName].indexOf(handler);
                                if(index > -1) {
                                    listeners[eventName].splice(index, 1);
                                }
                            }
                        });
        
                        return self;
                    }
        
                    this.fire = fire;
                    this.on = on;
                    this.off = off;
                    this.$get = function() {
                        return self;
                    };
                });
        

        【讨论】:

          猜你喜欢
          • 2015-02-05
          • 2022-01-27
          • 1970-01-01
          • 1970-01-01
          • 2016-06-10
          • 2013-04-10
          • 2018-08-23
          • 1970-01-01
          相关资源
          最近更新 更多