Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

angular JS - communicate between non-dependend services

Tags:

angularjs

I am new in angular and encounter a catch-22:

Facts:

  1. I have a service that logs my stuff (my-logger).

  2. I have replaced the $ExceptionHandler (of angular), with my own implementation which forwards uncaught exceptions to my-logger service

  3. I have another service, pusher-service, that needs to be notified whenever a fatal message is to be logged somewhere in my application using 'my-logger'.

Problem:

I can't have 'my-logger' be depend on 'pusher' since it will create circular dependency (as 'pusher' uses $http. The circle: $ExceptionHandler -> my-logger -> pusher -> $http -> $ExceptionHandler...)

My attempts:

In order to make these 2 services communicate with each other, I wanted to use $watch on the pusher-service: watches a property on $rootscope that will be updated in my-logger. But, when trying to consume $rootScope in 'my-logger', in order to update the property on which the 'pusher' "watches", I fail on circular dependency as it turns out that $rootscope depends on $ExceptionHandler (the circle: $ExceptionHandler -> my-logger -> $rootScope -> $ExceptionHandler).

Tried to find an option to get, at runtime, the scope object that in its context 'my-logger' service works. can't find such an option.

Can't use broadcast as well, as it requires my-logger to get access to the scope ($rootScope) and that is impossible as seen above.

My Question:

Is there an angular way to have two services communicate through a 3rd party entity ?

Any idea how this can be solved ?

like image 628
aviad cohen Avatar asked Mar 10 '13 13:03

aviad cohen


2 Answers

Use a 3rd service that acts as a notification/pubsub service:

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

Above, I only show one event/message type. Each additional event/message would need its own array, publish method, and subscribe method.

.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 would be coded similarly to Service1.

Notice how $rootScope, $broadcast, and scopes are not used with this approach, because they are not needed with inter-service communication.

With the above implementation, services (once created) stay subscribed for the life of the app. You could add methods to handle unsubscribing.

In my current project, I use the same NotificationService to also handle pubsub for controller scopes. (See Updating "time ago" values in Angularjs and Momentjs if interested).

like image 143
Mark Rajcok Avatar answered Oct 24 '22 08:10

Mark Rajcok


Yes, use events and listeners.

In your 'my-logger' you can broadcast an event when new log is captured:

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

and than listen for that event in your 'pusher':

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

This way you don't need to have any dependencies.

like image 31
Stewie Avatar answered Oct 24 '22 09:10

Stewie