2013-03-10 6 views
12

: Ich bin in Winkel- und stoßen auf eine Catch-22:Winkel JS - Kommunikation zwischen nicht-abhängige Dienste

Fakten:

  1. Ich habe einen Dienst, der meine Sachen anmeldet (my- Logger).

  2. Ich habe die $ Exception (Winkel), mit meiner eigenen Implementierung ersetzt, die

  3. abgefangene Ausnahmen von my-Logger-Dienst weiterleitet habe ich einen anderen Dienst, Drücker-Service, die, wann immer ein benachrichtigt muss fatale Nachricht soll irgendwo in meiner Anwendung mit 'my-logger' protokolliert werden.

Problem.

ich nicht 'my-Logger' haben auf 'Pusher' hängen, da es zirkuläre Abhängigkeit verwendet http $ schaffen wird (als 'Schieber' Der Kreis: $ Exception -> my-Logger -> Drücker -> $ http -> $ Exception ...)

Meine Versuche:

um diese 2-Dienste miteinander kommunizieren zu machen, ich wollte verwenden $ Uhr auf dem Pusher-Dienst: beobachtet eine Eigenschaft auf $ rootscope, die in meinem Logger aktualisiert wird. Aber wenn ich versuche, $ rootScope in 'meine-Logger' zu konsumieren, um die Eigenschaft zu aktualisieren, auf der der 'Pusher' "wacht", scheitert ich bei der Kreisabhängigkeit, da $ rootscope von $ ExceptionHandler (der Kreis: $ ExceptionHandler -> Mein-Logger -> $ rootScope -> $ ExceptionHandler).

Versucht, eine Option zu finden, um zur Laufzeit das Scope-Objekt zu erhalten, das in seinem Kontext 'my-logger' Dienst funktioniert. kann eine solche Option nicht finden.

Kann Broadcast auch nicht verwenden, da es my-Logger benötigt, um Zugriff auf den Bereich ($ rootScope) zu bekommen, und das ist unmöglich, wie oben zu sehen.

Meine Frage:

Gibt es eine Winkel Art und Weise zwei Dienste haben eine 3rd-Party-Unternehmen kommunizieren über?

Irgendeine Idee, wie das gelöst werden kann?

Antwort

6

Ja, verwenden Sie Ereignisse und Listener.

In Ihrem 'my-Logger' können Sie ein Ereignis übertragen, wenn neue Protokoll erfasst:

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

und als für dieses Ereignis in Ihrem 'Pusher' hören:

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

Auf diese Weise Sie müssen keine Abhängigkeiten haben.

+0

Nein, ich kann es nicht. 'my-logger' kann $ rootScope nicht verwenden, da $ rootScope alleine von $ ExceptionHandler abhängt, was zu der zirkulären Abhängigkeit führt ($ ExceptionHandler -> 'mein-logger' -> $ rootScope -> $ ExceptionHandler). Wie in der ursprünglichen Nachricht angegeben, konnte keine der Funktionen des Oszilloskops verwendet werden: Broadcast, emit, watch ... da 'mein-Logger' aufgrund der zirkulären Abhängigkeit keinen Zugriff darauf haben kann. –

1

Es ist mir teilweise gelungen, den Fall zu lösen: Ich habe die Abhängigkeit zwischen 'my-Logger' und 'Pusher' mit dem $ Injektor erstellt. Ich habe $ injector in 'my-logger' und injected bei "runtime" (bedeutet rechts, wenn es verwendet werden soll und nicht bei der Deklaration des Dienstes) den Pusher-Dienst bei fataler Nachrichtenankunft. Das funktionierte nur dann gut, wenn ich auch "runtime" das $ http zum "pusher" injiziert habe, kurz bevor das Versenden stattfinden soll.

Meine Frage ist, warum es mit Injektor in "Laufzeit" funktioniert und nicht mit den Abhängigkeiten am Kopf des Dienstes deklariert?

Ich habe nur eine Vermutung: seine eine Frage des Timings: Wenn Service auf „Laufzeit“ eingespritzt wird, wenn seine bereits vorhanden ist (Mittel bereits anders initialisiert wurde, wo), dann ist es keine Notwendigkeit zu holen und bekommen Alle seine Abhängigkeiten und damit der Kreis wird nie entdeckt und stoppt nie die Ausführung.

Bin ich richtig?

10

einen dritten Dienst verwenden, der als Benachrichtigung/PubSub Dienst wirkt:

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

Oben, zeige ich nur ein Ereignis/Nachrichtentyp. Jedes zusätzliche Ereignis/jede zusätzliche Nachricht benötigt ein eigenes Array, eine publish-Methode und eine subscribe-Methode.

.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 ähnlich Dienste1 codiert werden würde.

Beachten Sie, dass $ rootScope, $ broadcast und Bereiche nicht mit diesem Ansatz verwendet werden, da sie bei der dienstübergreifenden Kommunikation nicht benötigt werden.

Mit der obigen Implementierung bleiben Dienste (einmal erstellt) für die Lebensdauer der App abonniert. Sie könnten Methoden hinzufügen, um das Abmelden zu umgehen.

In meinem aktuellen Projekt verwende ich den gleichen NotificationService, um auch Pubsub für Controller-Bereiche zu behandeln. (Siehe Updating "time ago" values in Angularjs and Momentjs bei Interesse).

+0

Ein Plünderer dafür würde sehr geschätzt, wenn möglich. Ich habe Schwierigkeiten, dies vollständig zu verstehen –

+0

Ich habe eine Frage dazu bei. http://stackoverflow.com/questions/35293132/pub-sub-design-pattern-angularjs-service Bitte schauen Sie, wenn Sie Zeit haben.Grundsätzlich würde ich gerne ein funktionierendes Beispiel dafür sehen, um es besser verstehen zu können. Prost –

0

Dies ist eine einfache Art und Weise zu veröffentlichen/zu abonnieren, um mehrere Ereignisse zwischen den Diensten und Controller

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

Konsumenten und Produzenten

.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

Sie können Ihren eigenen generisches Ereignis Verlag Service machen, und injiziert es in jeden Dienst.

Hier ist ein Beispiel (ich habe es nicht getestet, aber Sie erhalten die Idee):

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