Apart from being able to use in-built directives like ng-click to handle DOM events, AngularJS also allows us to create our own events and propagate them to other controllers. Consider an example where you would like to make an AJAX call to retrieve some data from an API. Now, assume you have a controller that fetches the data and another controller to display the data. In this scenario, once the first controller fetches the data, it can notify other controllers that it’s arrived and send the data. AngularJS does this by emitting ($emit) and broadcasting ($broadcast) the events.
So, essentially there are two ways of propagating events in AngularJS:
Why do we have two ways to propagate an event?
Before we answer that question, lets try to understand what is scope hierarchy in AngularJS. Let’s say you have a view with multiple scopes defined on various div elements like in the example below:
As we understand, each of the controllers above will have their own scope and we can see an hierarchy of scopes.
Emitting allows us to propagate an event upwards in the scope hierarchy and broadcasting allows us to propagate an event downwards in the scope hierarchy.
In the example above, emitting would allow us to propagate an event from grandchild to child to parent controllers. Similarly, broadcasting would allow us to propagate an event from parent to child to grandchild.
Lets try to understand this in more detail using a diagram.
Now that we understand how emitting and broadcasting works, lets dive into their syntax and how to use them.
The $scope has a function called $emit() that’s used to emit an event upwards in the scope hierarchy. The event life cycle starts with the scope on which $emit() was called and is dispatched upwards in the scope hierarchy to all the registered listeners. We shall learn about registered listeners a little later in this post.
The first parameter to the $emit() function is the name of the event that is being emitted. The scopes that want to get notified when the event occurs should register listeners with this event name. After the first parameter we can pass multiple parameters to $emit(), which will then be passed to the event listeners. Typically we will pass the data which should be shared with the listeners here.
Similar to $emit, the first parameter is the name of the event that is being broadcast and we can pass data as args in the second parameter. The major difference as learnt earlier being, the event propagates downwards in the scope hierarchy to all the child scopes.
We now know how to emit or broadcast an event. But, the next question would be how to catch that event and consume the data that is being passed. To handle this, there is an $on function on scope using which we can register to listen to an event.
The $on function registers event listeners that should be called when the event occurs. The first parameter is the name of the event you are interested in. The second parameter (handlerFunction) is a callback function which gets called when the event occurs.
The call back function takes two parameters:
- Event : This is an event object which has several useful properties and functions that give more information about the event. These are as follows:
- name: The name of the event that’s broadcasted or emitted.
- targetScope: The scope that emitted/broadcasted the event.
- currentScope: The scope that’s handling the event.
- stopPropagation: This is a function, which when called, stops further propagation of the event. But keep in mind this is available only in the events that were emitted. After an event is emitted, if a particular scope calls this function the event propagation stops. However, once an event is broadcast it can’t be stopped. It’s like this because the broadcast events can span across multiple branches as a parent scope may have multiple child scopes. So, if a scope in a particular branch stops the event it will still keep propagating in other branches. But in case of $emit the event propagates from child to parent and if it’s stopped somewhere in between, it stops propagating further right there.
- Args : One or more arguments that represent the data that was emitted/broadcast as a part of the event.
What if you have to notify an event to all child scopes?
If you come across a situation where in you have to notify all the scopes of a particular event, you’ve got $rootScope at your rescue. We can treat $rootScope as a central message bus. So, if there is an event that affects all the child scopes, you can broadcast the event on $rootScope and then let the children handle the event.
Another important event that we need to understand as part of events in AngularJS is the $destroy event.
When a scope is being destroyed a $destroy event is broadcast on the scope. We can listen for this event and perform any necessary cleanups. For example, if you have a timer setup from the $timeout service we can cancel it so that it won’t consume CPU cycles unnecessarily. We can also use this to cancel watchers and any custom event listeners that we may have set up earlier. When a scope is getting destroyed it also means that any digest cycle won’t touch this and its child scopes anymore. As a result, the scope becomes eligible for garbage collection and the memory can be reclaimed.
Following is the syntax to use $destroy event:
//clean up code