13

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

事实:

  1. 我有一个记录我的东西的服务(我的记录器)。

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

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

问题:

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

我的尝试:

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

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

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

我的问题:

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

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

4

5 回答 5

10

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

.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 和 scopes 是如何不用于这种方法的,因为它们在服务间通信中是不需要的。

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

在我当前的项目中,我使用相同的 NotificationService 来处理控制器范围的 pubsub。(如果有兴趣,请参阅更新 Angularjs 和 Momentjs 中的“时间前”值)。

于 2013-08-10T18:21:06.687 回答
6

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

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

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

而不是在你的“推动者”中听那个事件:

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

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

于 2013-03-10T15:11:14.013 回答
1

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

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

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

我对么 ?

于 2013-03-13T11:19:47.640 回答
0

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

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

        .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;
            };
        });
于 2016-12-09T17:50:21.157 回答
0

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

.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);
}])
于 2016-10-26T09:48:57.193 回答