angular JS - общение между независимыми сервисами

Я новичок в угловых и сталкиваюсь с ловушкой-22

Факты:

  1. У меня есть сервис, который регистрирует мои вещи (my-logger).

  2. Я заменил $ExceptionHandler (angular) своей собственной реализацией, которая пересылает необработанные исключения в службу my-logger

  3. У меня есть другой сервис, pusher-service, о котором нужно уведомлять всякий раз, когда в моем приложении регистрируется неустранимое сообщение где-то в моем приложении с помощью my-logger.

Проблема:

У меня не может быть 'my-logger', зависящего от 'pusher', поскольку он создаст циклическую зависимость (так как 'pusher' использует $http. Круг: $ExceptionHandler -> my-logger -> pusher -> $http -> $ExceptionHandler...)

Мои попытки:

Чтобы эти 2 службы взаимодействовали друг с другом, я хотел использовать $ watch на сервисе pusher: следит за свойством на $ rootscope, которое будет обновлено в my-logger. Но, когда я пытаюсь использовать $ rootScope в 'my-logger', чтобы обновить свойство, на которое "смотрит" пушер, у меня не получается циклическая зависимость, поскольку оказывается, что $ rootcope зависит от $ExceptionHandler (круг: $ExceptionHandler -> my-logger -> $rootScope -> $ExceptionHandler).

Попытался найти вариант для получения во время выполнения объекта области действия, который в своем контексте работает в сервисе my-logger. не могу найти такой вариант.

Также нельзя использовать широковещательную рассылку, поскольку для получения доступа к области действия ($rootScope) требуется my-logger, а это невозможно, как показано выше.

Мой вопрос:

Существует ли угловой способ связи двух служб через сторонний объект?

Есть идеи, как это можно решить?

5 ответов

Используйте 3-й сервис, который действует как сервис уведомлений / pubsub:

.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 для областей контроллера. (См. Обновление значений времени назад в Angularjs и Momentjs, если интересно).

Да, использовать события и слушателей.

В вашем "my-logger" вы можете транслировать событие при захвате нового журнала:

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

и чем слушать это событие в вашем "толкаче":

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

Таким образом, вам не нужно иметь никаких зависимостей.

Мне частично удалось решить дело: я создал зависимость между 'my-logger' и 'pusher' с помощью $ инжектора. Я использовал $injector в 'my-logger' и вводил во время выполнения (означает, что именно тогда, когда он собирается использоваться, а не в объявлении службы), в службу-толкач при поступлении фатального сообщения. Это работало хорошо только тогда, когда я также вводил во время выполнения $http в 'pusher' прямо перед отправкой.

У меня вопрос, почему он работает с инжектором в "среде выполнения", а не с зависимостями, объявленными во главе службы?

У меня есть только одно предположение: это вопрос времени: когда служба вводится во время выполнения, если она уже существует (значит, уже была инициализирована где-то еще), то нет необходимости извлекать и получать все ее зависимости, и поэтому круг никогда не обнаруживается и никогда не останавливает выполнение.

Я прав?

Вы можете создать свою собственную общую службу публикации событий и внедрить ее в каждую службу.

Вот пример (я не проверял это, но вы поняли):

        .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;
            };
        });

Это простой способ публикации / подписки на несколько событий между службами и контроллерами.

.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);
}])
Другие вопросы по тегам