angular JS - общение между независимыми сервисами
Я новичок в угловых и сталкиваюсь с ловушкой-22
Факты:
У меня есть сервис, который регистрирует мои вещи (my-logger).
Я заменил $ExceptionHandler (angular) своей собственной реализацией, которая пересылает необработанные исключения в службу my-logger
У меня есть другой сервис, 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);
}])