Подтвердить что ты не робот

Angular JS - связь между службами, не зависящими от них

Я новичок в angular и сталкиваюсь с catch-22:

Факты:

  • У меня есть служба, которая регистрирует мои файлы (my-logger).

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

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

Проблема:

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

Мои попытки:

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

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

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

Мой вопрос:

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

Любая идея, как это можно решить?

4b9b3361

Ответ 1

Используйте третью службу, которая действует как служба уведомления /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 и scopes, потому что они не нужны с межсервисной связью.

С приведенной выше реализацией службы (после создания) остаются подписчиками на всю жизнь приложения. Вы можете добавлять методы для отмены подписки.

В моем текущем проекте я использую тот же NotificationService, чтобы также обрабатывать pubsub для областей контроллера. (См. Обновление значений времени и времени в Angularjs и Momentjs, если они заинтересованы).

Ответ 2

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

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

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

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

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

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

Ответ 3

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

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

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

Правильно ли я?

Ответ 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;
            };
        });