2013-03-10 11 views
12

Jestem nowy w kanciaste i spotkać catch-22:kątowe JS - komunikować się między usługami non-dependend

fakty:

  1. mam usługę, która rejestruje moje rzeczy (my- rejestrator).

  2. jakie otrzymuje $ ExceptionHandler (kątowa) z własnej implementacji który przekazuje niezłapane wyjątki serwisowego my-logger

  3. mam inną usługę popychacza-Service, który musi być powiadomiony, ilekroć fatalna wiadomość ma być logowana gdzieś w mojej aplikacji za pomocą "my-logger".

Problem:.

nie mogę mieć '' my-logger można polegać 'popychacz', ponieważ stworzy to zależność cykliczna (jako 'popychacz' używa $ http Krąg: $ ExceptionHandler -> my-logger -> pchacz -> $ http -> $ ExceptionHandler ...)

Moje próby:

aby te 2 usługi komunikują się ze sobą, chciałem użyć $ watch w usłudze pusher: obserwuje właściwość na $ rootscope, która zostanie zaktualizowana w my-logger. Ale gdy próbujemy zużyć $ rootScope w 'moim-loggerze', aby zaktualizować właściwość, na której "zegarmistrz" "obserwuje", nie działa zależność cykliczna, ponieważ okazuje się, że $ rootscope zależy od $ ExceptionHandler (circle: $ ExceptionHandler -> my-logger -> $ rootScope -> $ ExceptionHandler).

Próbowano znaleźć opcję, aby w czasie wykonywania uzyskać obiekt zakresu, który działa w jego kontekście jako usługa "mój logger". nie mogę znaleźć takiej opcji.

Nie można również używać transmisji, ponieważ wymaga ona, aby mój logger uzyskał dostęp do zakresu ($ rootScope) i jest to niemożliwe, jak pokazano powyżej.

Moje pytanie:

Czy istnieje sposób mieć kątowe dwóch usług komunikować poprzez 3rd podmiotowi strony?

Każdy pomysł, jak można to rozwiązać?

Odpowiedz

6

Tak, korzystaj z wydarzeń i słuchaczy.

W swoim „my-logger” można nadawane zdarzenie, kiedy nowy dziennik zostaje schwytany:

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

i niż słuchać o tym wydarzeniu w swoim „popychacz”:

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

ten sposób nie potrzebujesz żadnych zależności.

+0

Nie, nie mogę. "mój-logger" nie może używać $ rootScope, ponieważ $ rootScope sam zależy od $ ExceptionHandler, który doprowadzi do zależności cyklicznej ($ ExceptionHandler -> "my-logger" -> $ rootScope -> $ ExceptionHandler). Jak stwierdzono w oryginalnym komunikacie, nie można użyć żadnej z możliwości tego zakresu: rozgłaszanie, emitowanie, oglądanie ... ponieważ "mój logger" nie może mieć do niego żadnego dostępu z powodu zależności cyklicznej. –

1

Częściowo udało mi się rozwiązać sprawę: Stworzyłem zależność między "my-logger" i "pusher" przy użyciu wtryskiwacza $. Użyłem $ injector w 'mój-logger' i wstrzyknięto w "runtime" (oznacza to prawo, gdy ma być użyty, a nie w deklaracji usługi) usługę pchacza po przybyciu wiadomości. To działało dobrze tylko wtedy, gdy wstrzyknąłem w "czasie wykonywania" $ http do "pchacza" tuż przed wysłaniem.

Moje pytanie brzmi: dlaczego działa z wtryskiwaczem w "trybie pracy", a nie z zależnościami zadeklarowanymi na czele usługi?

Mam tylko jedno przypuszczenie: jego kwestia czasu: Gdy usługa jest wstrzykiwany przy „starcie”, jeśli jej już istnieje (środek został już zainicjowany gdzie indziej), a następnie jest ma potrzeby pobrać i uzyskać wszystkie jego zależności, a więc krąg nigdy nie został odkryty i nigdy nie zatrzymuje wykonania.

Czy mam rację?

10

Użyj 3rd serwis, który działa jako usługa powiadamiania/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); 
     } 
    }; 
}]) 

Powyżej, tylko pokazać jeden rodzaj zdarzenia/wiadomość. Każde dodatkowe zdarzenie/wiadomość wymagałoby własnej tablicy, metody publikowania i metody subskrypcji.

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

Usługa2 będą kodowane podobnie do Service1.

Zobacz, jak $ rootScope, $ broadcast i zakresy nie są używane z tym podejściem, ponieważ nie są potrzebne w komunikacji między usługami.

Dzięki powyższej implementacji usługi (po utworzeniu) pozostają zasubskrybowane przez cały okres eksploatacji aplikacji. Możesz dodać metody obsługi anulowania subskrypcji.

W moim bieżącym projekcie używam tej samej usługi NotificationService, aby również obsługiwać pubsub dla zakresów kontrolerów. (Zobacz Updating "time ago" values in Angularjs and Momentjs, jeśli zainteresowany).

+0

Pijak za to byłby bardzo doceniany, jeśli to możliwe. Mam problem z jego zrozumieniem. –

+0

Stworzyłem pytanie na ten temat na stronie. http://stackoverflow.com/questions/35293132/pub-sub-design-pattern-angularjs-service, proszę spojrzeć, jeśli masz czas.Zasadniczo chciałbym zobaczyć działający plunker tego przykładu, aby lepiej to zrozumieć. Twoje zdrowie –

0

Jest to prosty sposób opublikować/subscribe do wielu zdarzeń między usługami i sterownikami

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

konsumenci i producenci

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

można zrobić własną usługę wydawcy generic zdarzeń i wstrzyknąć go do każdej usługi.

Oto przykład (nie testowałem go, ale masz pomysł):

 .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; 
      }; 
     }); 
Powiązane problemy