Angular $broadcast vs. $emit - diffs, pros and cons

Goal:

There always seems to be confusion over when to use $broadcast vs. $emit and can be hard to remember the scope affected by which one. Of course this will also have an impact on performance.

Details:

In either case, $broadcast or $emit, Angular uses $rootScope.$on and $scope.$on to listen to these events. Which one to use is up to the how far you want to expose the listener or subscriber to dispatched events.

  • $rootScope

    • $emit

      dispatches events upwards only by traversing the scope hierarchy from the bottom-up. Only $rootScope listeners will be notified of any changes since it is also the “root” scope. That means any $scope subscribers will not be notified. A simile of this would be when there are grandparents, parents and children in the room, the grandparents speak to each other but neither their children nor grandchildren here the conversation ~ old people only.
    • $broadcast

      dispatches the event downwards to all child scopes, regardless of how deep it hierarchy is. This is a truly expensive exercise and the digest cannot be cancelled. As long as any object is $dirty, the digest lifecycle will continue to cycle, up to 10 times until no more changes are found. A simile of this would be when there are grandparents, parents and children in the room, the grandparents let everyone to come eat dinner, the message is intended for all in the room (everyone as in all scopes in the ng-app).
  • $scope

    • $emit

      dispatches events upwards only by traversing the scope hierarchy from the bottom-up. This is intended to dispatch events to parents, all the way to the eventual $rootScope. However, none of the other children get notified. Let’s say you have a room with great-grandparents, grandparents, parents and children, four generations, 4-level deep scope and one of the children wants to share a secret with all their parents, grandparents and finally great-grandparents ($rootScope). None of the other children heard a thing!
    • $broadcast

      dispatches the event downwards to all child scopes, regardless of how deep it hierarchy is, but only to the $scope and not any of the $rootScope listeners. A simile would be when there are grandparents, parents and children in the room. Grandpa wants to share a Christmas gift idea with all of his children an children’s children, and so on, but he does not want grandma to know about it, it’s a surprise!

Here is a visual diagram to try and explain the scope impact:

image

Here is an example of $scope broadcasting and emitting and having one listener/subscriber listen to the event on $scope:

// displatch the event upwards:

$scope.$emit('someEvent', {

       msg: ’some data object’

});

// dispatch the event downwards:

$scope.$broadcast('someEvent', {

      msg: ‘can be some complex object or function’ // string or complex objects

});

// listen for the event in $scope:

$scope.$on('someEvent', function (e, data) {

      console.log(data.msg); // 'Data to send'

});

In order to cancel an event from bubbling up when calling $emit, simply stop call stop propagation:

$scope.$on('someEvent', function (e, data) {

       e.stopPropagation();

});

Be careful with naming conventions when broadcasting or emitting, just as with global namespace pollution you could also pollute the pub-sub event namespaces. I recommend a simple namespace hierarchies where the root follows a class-name pattern(nouns) and the sub-level some action. For example, if you are going to share shopping cart data with others and perform different actions on the shopping cart on different controllers, you may have something like:

$scope.$broadcast('cart:add'[, data]);
$scope.$on('cart:add', function (e, data) {...});

$scope.$emit('cart:delete'[, data]);
$scope.$on('cart:delete', function (e, data) {...});

$scope.$emit('cart:checkout'[, data]);
$scope.$on('cart:checkout', function (e, data) {...});