Skip to main content
added 1476 characters in body
Source Link
Joseph
  • 25.2k
  • 2
  • 25
  • 37
  • As a factory, the dependency is explicit. The dependent knows it's there and Angular will throw if the dependency is not found during initialization.

    As a factory, the dependency is explicit. The dependent knows it's there and Angular will throw if the dependency is missing on initialization, unlike $rootScope where it only throws when you use it.

  • Factories are singletons. You don't have to worry about checking and using the existing or create a new one.

    Factories are singletons. You don't have to worry about checking and using the existing or create a new one.

  • You're not polluting the $rootScope nor depending on something that might not be there or have been overridden by something in a lower, enclosing scope. This is a headache to debug, especially without tools like Batarang or ngInspector.

    You're not polluting the $rootScope nor depending on something that might not be there or have been overridden by something in a lower, enclosing scope. This is a headache to debug, especially without tools like Batarang or ngInspector.

angular.module('SignalrModule', []);
  .constant('SIGNALR_EVENTS', {
    FOO_EVENT: 'fooevent',
    BAR_EVENT: 'barevent',
  })
  .factory('SignalrFactory', function(){
    return {
      publish: function(){...},
      subscribe: function(){...},
      unsubscribe: function(){...},
    };
  });

angular.module('app', ['SignalrModule'])
  .controller('MyController', function(SignalrFactory, SIGNALR_EVENTS){
    SignalrFactory.subscribe(SIGNALR_EVENTS.FOO_EVENT, function(){
      // on foo event
    });
  });

Now I did mention that angular has a built-in pub-sub system. We can just hook on to it so your controllers will simply use regular events over $rootScope (we're not adding stuff to $rootScope, just making it a relay for events). Not sure of the following works, but the concept is there.

angular.module('SignalrModule', []);
  .constant('SIGNALR_EVENTS', {
    FOO_EVENT: 'fooevent',
    BAR_EVENT: 'barevent',
  })
  .run(function($rootScope, SIGNALR_EVENTS){ 

    // Iterate through our registry of events
    Object.keys(SIGNALR_EVENTS).forEach(function(eventName){

      // Relay angular events to signalr over $rootScope
      $rootScope.$on(SIGNALR_EVENTS[eventName], function(){
        signalR.eventAggregator.publish(SIGNALR_EVENTS[eventName]);
      });

      // Relay signalr events to angular over $rootScope
      signalR.eventAggregator.subscribe(SIGNALR_EVENTS[eventName], function(e) {
        $rootScope.$emit(SIGNALR_EVENTS[eventName], e);
      });
    });
    
  });

angular.module('app', ['SignalrModule'])
  .controller('MyController', function($rootScope, SIGNALR_EVENTS){

    // Using regular angular-ish emit and on

    $rootScope.$emit(SIGNALR_EVENTS.FOO_EVENT, { foo: 'data' });

    $rootScope.$on(SIGNALR_EVENTS.BAR_EVENT, function(){
      // bar event emitted somewhere
    });
  });
  • As a factory, the dependency is explicit. The dependent knows it's there and Angular will throw if the dependency is not found during initialization.
  • Factories are singletons. You don't have to worry about checking and using the existing or create a new one.
  • You're not polluting the $rootScope nor depending on something that might not be there or have been overridden by something in a lower, enclosing scope. This is a headache to debug, especially without tools like Batarang or ngInspector.
angular.module('SignalrModule', []);
  .constant('SIGNALR_EVENTS', {
    FOO_EVENT: 'fooevent',
    BAR_EVENT: 'barevent',
  })
  .factory('SignalrFactory', function(){
    return {
      publish: function(){...},
      subscribe: function(){...},
      unsubscribe: function(){...},
    };
  });

angular.module('app', ['SignalrModule'])
  .controller('MyController', function(SignalrFactory, SIGNALR_EVENTS){
    SignalrFactory.subscribe(SIGNALR_EVENTS.FOO_EVENT, function(){
      // on foo event
    });
  });
  • As a factory, the dependency is explicit. The dependent knows it's there and Angular will throw if the dependency is missing on initialization, unlike $rootScope where it only throws when you use it.

  • Factories are singletons. You don't have to worry about checking and using the existing or create a new one.

  • You're not polluting the $rootScope nor depending on something that might not be there or have been overridden by something in a lower, enclosing scope. This is a headache to debug, especially without tools like Batarang or ngInspector.

angular.module('SignalrModule', []);
  .constant('SIGNALR_EVENTS', {
    FOO_EVENT: 'fooevent',
    BAR_EVENT: 'barevent',
  })
  .factory('SignalrFactory', function(){
    return {
      publish: function(){...},
      subscribe: function(){...},
      unsubscribe: function(){...},
    };
  });

angular.module('app', ['SignalrModule'])
  .controller('MyController', function(SignalrFactory, SIGNALR_EVENTS){
    SignalrFactory.subscribe(SIGNALR_EVENTS.FOO_EVENT, function(){
      // on foo event
    });
  });

Now I did mention that angular has a built-in pub-sub system. We can just hook on to it so your controllers will simply use regular events over $rootScope (we're not adding stuff to $rootScope, just making it a relay for events). Not sure of the following works, but the concept is there.

angular.module('SignalrModule', []);
  .constant('SIGNALR_EVENTS', {
    FOO_EVENT: 'fooevent',
    BAR_EVENT: 'barevent',
  })
  .run(function($rootScope, SIGNALR_EVENTS){ 

    // Iterate through our registry of events
    Object.keys(SIGNALR_EVENTS).forEach(function(eventName){

      // Relay angular events to signalr over $rootScope
      $rootScope.$on(SIGNALR_EVENTS[eventName], function(){
        signalR.eventAggregator.publish(SIGNALR_EVENTS[eventName]);
      });

      // Relay signalr events to angular over $rootScope
      signalR.eventAggregator.subscribe(SIGNALR_EVENTS[eventName], function(e) {
        $rootScope.$emit(SIGNALR_EVENTS[eventName], e);
      });
    });
    
  });

angular.module('app', ['SignalrModule'])
  .controller('MyController', function($rootScope, SIGNALR_EVENTS){

    // Using regular angular-ish emit and on

    $rootScope.$emit(SIGNALR_EVENTS.FOO_EVENT, { foo: 'data' });

    $rootScope.$on(SIGNALR_EVENTS.BAR_EVENT, function(){
      // bar event emitted somewhere
    });
  });
Source Link
Joseph
  • 25.2k
  • 2
  • 25
  • 37

Hmm... interesting use of run + $rootScope, but I would avoid it primarily because of $rootScope. It's essentially the "global space" of an Angular app. The only legit use I see for it is attaching event listeners ($rootScope.$on), and for broadcasting events ($rootScope.$emit).

As an alternative, you could wrap your event relay in a factory.

  • As a factory, the dependency is explicit. The dependent knows it's there and Angular will throw if the dependency is not found during initialization.
  • Factories are singletons. You don't have to worry about checking and using the existing or create a new one.
  • You're not polluting the $rootScope nor depending on something that might not be there or have been overridden by something in a lower, enclosing scope. This is a headache to debug, especially without tools like Batarang or ngInspector.

Additionally, you might also want to put your event names in a constant. That way, you have a global lookup of event names, and you won't be hardcoding string literals all over the app.

Here's an example of how it would look like as a factory (using implicit dependency injection syntax for brevity).

angular.module('SignalrModule', []);
  .constant('SIGNALR_EVENTS', {
    FOO_EVENT: 'fooevent',
    BAR_EVENT: 'barevent',
  })
  .factory('SignalrFactory', function(){
    return {
      publish: function(){...},
      subscribe: function(){...},
      unsubscribe: function(){...},
    };
  });

angular.module('app', ['SignalrModule'])
  .controller('MyController', function(SignalrFactory, SIGNALR_EVENTS){
    SignalrFactory.subscribe(SIGNALR_EVENTS.FOO_EVENT, function(){
      // on foo event
    });
  });