13

私はAngularが初めてで、catch-22に遭遇しました:

事実:

  1. 自分のものをログに記録するサービス (my-logger) があります。

  2. $ExceptionHandler (Angular の) を、キャッチされていない例外を my-logger サービスに転送する独自の実装に置き換えました。

  3. 「my-logger」を使用してアプリケーションのどこかに致命的なメッセージが記録されるたびに通知する必要がある別のサービス、プッシャーサービスがあります。

問題:

'my-logger' を 'pusher' に依存させることはできません。循環依存関係が作成されるためです ('pusher' は $http を使用するため)。円: $ExceptionHandler -> my-logger -> pusher -> $http -> $ExceptionHandler...)

私の試み:

これら 2 つのサービスが相互に通信できるようにするために、プッシャー サービスで $watch を使用したいと考えました。my-logger で更新される $rootscope のプロパティを監視します。しかし、「my-logger」で $rootScope を使用しようとすると、「プッシャー」が「監視」するプロパティを更新するために、循環依存関係で失敗します。これは、$rootscope が $ExceptionHandler (円: $ExceptionHandler -> my-logger -> $rootScope -> $ExceptionHandler)。

実行時に、そのコンテキストで「my-logger」サービスが機能するスコープ オブジェクトを取得するオプションを見つけようとしました。そのようなオプションが見つかりません。

my-logger がスコープ ($rootScope) にアクセスする必要があり、上記のように不可能であるため、ブロードキャストも使用できません。

私の質問:

2 つのサービスがサード パーティのエンティティを介して通信する角度のある方法はありますか?

これをどのように解決できるか考えていますか?

4

5 に答える 5

10

通知/pubsub サービスとして機能する 3 番目のサービスを使用します。

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

上記では、1 つのイベント/メッセージ タイプのみを示しています。追加のイベント/メッセージごとに、独自の配列、発行メソッド、および購読メソッドが必要になります。

.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 で「時間前」の値を更新するを参照してください)。

于 2013-08-10T18:21:06.687 に答える
6

はい、イベントとリスナーを使用します。

「my-logger」では、新しいログがキャプチャされたときにイベントをブロードキャストできます。

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

「プッシャー」でそのイベントをリッスンするよりも:

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

この方法では、依存関係を持つ必要はありません。

于 2013-03-10T15:11:14.013 に答える
1

このケースの解決に部分的に成功しました: $injector を使用して、'my-logger' と 'pusher' の間の依存関係を作成しました。「my-logger」で $injector を使用し、致命的なメッセージの到着時にプッシャー サービスを「実行時」 (サービスの宣言時ではなく、使用しようとしているときを意味します) に注入しました。これは、送信が行われる直前に「実行時」に $http を「プッシャー」に注入した場合にのみうまく機能しました。

私の質問は、サービスの先頭で宣言された依存関係ではなく、「実行時」にインジェクターで動作する理由です。

私は1つだけ推測しています:それはタイミングの問題です:サービスが「実行時」に注入されるとき、それがすでに存在する場合(他の場所ですでに初期化されていることを意味します)、すべての依存関係をフェッチして取得する必要はなく、したがって円は検出されず、実行が停止されることはありません。

私は正しいですか?

于 2013-03-13T11:19:47.640 に答える
0

これは、サービスとコントローラーの間で複数のイベントを発行/サブスクライブする簡単な方法です

.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);
}])
于 2016-10-26T09:48:57.193 に答える
0

独自の汎用イベント パブリッシャー サービスを作成し、それを各サービスに挿入できます。

ここに例があります(私はそれをテストしていませんが、あなたはアイデアを得るでしょう):

        .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;
            };
        });
于 2016-12-09T17:50:21.157 に答える