Naor
Naor

Reputation: 24103

$rootScope as event aggregator

I am using angular-js. I have a service that need to trigger event each time something occurs. For doing it I need an object that will act as event aggregator.

  1. Do I need to build one? Or should I use the $rootScope?
  2. In case I should use $rootScope, how can I ensure that there is no event names conflicts?
  3. Is it efficient to use $rootScope for events that don't need them to propagate to child scopes?

Upvotes: 5

Views: 3397

Answers (2)

g00fy
g00fy

Reputation: 4737

Take a look at http://docs.angularjs.org/api/ng.$rootScope.Scope#$broadcast.

  1. Using $rootScope as event aggregator is perfectly fine unless you are triggering events outslide digest cycle or triggering multiple (100+) events at the same time where some other solutions may be more appropriate .
  2. One accepted practice would be to use namespacing (namespace:event - pattern used by Backbone.Marionette)
  3. Use $emit on a child scope instead of $broadcast on $rootScope - $emit propagates only upwards, where $broadcast propagates downwards - to all children

Upvotes: 4

yuxhuang
yuxhuang

Reputation: 5389

I modeled and implemented the following mechanism in a web project for tablets:

  1. Define notifications in your services. I did not want to use the term events since I did not want it to be confused with DOM events by other developers in my team. And a semi-typed name for a notification is easier for IDE with intellisense support and for debugging. For example, I have one Device service that would $broadcast(Device.Notification.OrientationDidChange) when a tablet device's orientation is changed.

  2. Use Scope objects to $broadcast or $emit notifications, depending on your need. For example,

    • For global notifications like the previous one, I do: $rootScope.$broadcast(Device.Notification.OrientationDidChange). So all listeners can listen on their own scope without injecting $rootScope.
    • For local notifications that could possibly only affect the current scope (and its children), such as a notification that a scope needs to tell all its child scopes that the layout of the current controller is changed, usually I do: scope.$broadcast(UI.Notification.NeedsLayout), where UI is a predefined service to hold UI related constants, and scope is the current scope.
    • For certain notifications that a child scope needs to send upwards, for example, a slider directive that tells ascendant scopes that the rangeStart value has changed (in addition to regular two way binding), I use: scope.$emit(Slider.Notification.RangeStartDidChange), where scope is the current scope.
  3. This approach is a bit verbose in a small project. You might want to stick to using $rootScope.$emit(Notification) all the time, and let all listeners to do $rootScope.$on(Notification, callback) to receive these notifications.

  4. In some situations, you might want to define these notifications in a centralized service in order to more easily avoid name conflicts. It really based upon your project's naming convention.

  5. The implementation (actual values) of these notifications may vary. I prefer using strings.

  6. With $broadcast or $emit you can also actually pass additional arguments to the listeners, for example, $broadcast(Notification, arg1, arg2)... Angular's documentation is quite detailed.

Upvotes: 13

Related Questions