This blog is in continuation to my
previous blog post, so if you haven't read the previous blog at least have a look at the
code here as I am not going to embed all the code snippets again in this blog.
As we already know about what component events are and we have also made a small application using component events in previous post. It's time to modify that application a little and learn more about types of phases in component events. You must have read earlier about Bubble and Capture phase in component events in my blog post
Salesforce Lightning Events Part 1 - Component Events Introduction. We are going to see these phases in action here.
So, If you remember the
code used in my
previous blog, I have made a simple component event which will run by default in bubble phase to calculate the total income from the entries present in lightning datatable and pass that total income to the parent component from the source component using an event by clicking on
Calculate Total Income Button. When you click on this button, you see the output like this:-
|
Output 1 - Alert message from source component that fired the event |
This simply means that, as the lightning event traverse in
bubble phase by default, therefore the handler of source component that fired the event is executed first and so we get the alert message -
Event handler at source component that fired the event. Moving on to the second output which looks like below:-
|
Output 2 - Alert message from parent component |
This is the event handler defined at the parent component which is executed when the handler at the source component is already executed. As in the bubble phase, the handler at the source component is executed first and then the event is traversed up in the hierarchy and the handler at the parent component is executed.
({
// Function to handle Lightning Event
handleTotalIncomeComponentEvent : function(component, event, helper) {
alert('Event handler at the parent component');
// Getting the value of totalIncome attribute of event using event.getParam()
var totalIncome = event.getParam('totalIncome');
// Setting the totalIncome attribute of component with the event's totalIncome attribute value.
component.set('v.totalIncome', totalIncome);
}
})
If you remember, the handler defined at parent component as shown above, after the alert message, we set the value of
totalIncome argument. So, we have a final output like this:-
|
Output 3 - Final change in the totalIncome variable by the parent component handler |
Understanding Bubble Phase in Component Event
Now, let's make a small change in event handler in our source component i.e. in file
LightningEventsComp1Controller.js, right now you have the following code:-
| // Function to handle Lightning Event fired from this component itself. |
| handleRegisteredComponentEvent: function(component, event, helper) { |
| alert('Event handler at source component that fired the event.'); |
| }, |
I am going to add one more line in this code and that is
event.stopPropagation(); So, my final code is like given below:-
| // Function to handle Lightning Event fired from this component itself. |
| handleRegisteredComponentEvent: function(component, event, helper) { |
| alert('Event handler at source component that fired the event.'); |
| event.stopPropagation(); |
| }, |
Now when you run this app, you'll see that only output 1 is there and output 2 and 3 didn't came. So, what's the reason behind that ? You guessed it right..!! By default the event is propagating in
Bubble Phase therefore, when we added
event.stopPropagation(); in the handler defined in source component, the event stopped propagating and it didn't reached the parent component and so we didn't got output 2 and 3. We got only
Output 1 in this case.
Okay, so we got the fact that how the events propagate in the bubble phase. Now it's time to move on to the capture phase. Remove the
event.stopPropagation(); line so that we are back in the previous state where we were getting all the outputs 1, 2 and 3.
Understanding Capture Phase in Component Event
In our code, firstly move to LightningEventsComp1.cmp file and checkout the handler for event. You'll see something like this:-
| <aura:handler name="totalIncomeComponentEvent" event="c:LightningComponentEvent" action="{!c.handleRegisteredComponentEvent}"></aura:handler> |
Now, in the above code, I am going to add one extra attribute in the handler i.e. the final code is like given below:-
| <aura:handler name="totalIncomeComponentEvent" event="c:LightningComponentEvent" action="{!c.handleRegisteredComponentEvent}" phase="capture"></aura:handler> |
Notice that, I have added another attribute as
phase="capture" in the event handler specifying that this event handler will now handle the capture phase of the fired event. Similarly, make this change in the handler present in
LightningEventsCompContainer.cmp file. The initial and final versions of the second file are given below:-
Initial Version
| <aura:handler name="totalIncomeComponentEvent" event="c:LightningComponentEvent" action="{!c.handleTotalIncomeComponentEvent}"></aura:handler> |
Final Version
| <aura:handler name="totalIncomeComponentEvent" event="c:LightningComponentEvent" action="{!c.handleTotalIncomeComponentEvent}" phase="capture"></aura:handler> |
Now, as you can see in the final version of both the files, the handlers have added the attribute
phase="capture". Now let's click on
Calculate Total Income button again and check the changes in
outputs. You'll see that the order of the output is
Output 2 ->
Output 1 ->
Output 3. Now, we know that in capture phase the event start propagating from the application root. Therefore, the
Output 2 i.e. alert in parent component appear before the alert in source component.
Notice that even the source component is firing the event and it has a handler defined to handle that event still the handler of parent component handles the event first because as the event is fired, it directly go to the application root without the handler being executed at the source component (if any) and then traverse down to the source component that fired the event.
There is a matter of surprise that the
Output3 i.e. the updating the total income value should have occurred after output 2 and then output 1 should occur. Yes, you are right and this is only how it's happening but the point is that as the js updated the value of total income it didn't stop executing and the next alert message came up before the effect of change in
totalIncome is reflected.
The parent component handler is executed before the source component.
To make sure this concept you can add
event.stopPropagation(); to the parent component handler function as given below:-
LightningEventsCompContainerController.js - Before adding stop propagation method
| ({ |
| // Function to handle Lightning Event |
| handleTotalIncomeComponentEvent : function(component, event, helper) { |
| alert('Event handler at the parent component'); |
| // Getting the value of totalIncome attribute of event using event.getParam() |
| var totalIncome = event.getParam('totalIncome'); |
| // Setting the totalIncome attribute of component with the event's totalIncome attribute value. |
| component.set('v.totalIncome', totalIncome); |
| } |
| }) |
LightningEventsCompContainerController.js - After adding stop propagation method
| ({ |
| // Function to handle Lightning Event |
| handleTotalIncomeComponentEvent : function(component, event, helper) { |
| alert('Event handler at the parent component'); |
| // Getting the value of totalIncome attribute of event using event.getParam() |
| var totalIncome = event.getParam('totalIncome'); |
| // Setting the totalIncome attribute of component with the event's totalIncome attribute value. |
| component.set('v.totalIncome', totalIncome); |
| event.stopPropagation(); |
| } |
| }) |
You'll see that the child component handler is not executed as the event stopped propagating after reaching the parent component.So, the order of the output is as follows:-
Output 2 -> Output 3.
So, we have seen the bubble phase as well as the capture phase and also the path the event follow in both the phases. But wait, these phases are only associated with event handler right..!!
What will happen when I configure one handler to handle bubble phase and other to handle capture phase ?? Let's figure it out.
Remove the
event.stopPropagation(); so that we can continue again from the initial state
. As the event run in both the phases, you'll see no change as both the handlers will run when you run one handler in bubble and other in capture phase. But, notice that the handler which is in capture phase run first and the handler which is in bubble phase run after that.
Suppose you only add
phase="capture" to
LightningEventsComp1.cmp and the
LightningEventsCompContainer.cmp runs in
bubble phase as default. Given below is the handler in both the files:-
LightningEventsComp1.cmp
| <aura:handler name="totalIncomeComponentEvent" event="c:LightningComponentEvent" action="{!c.handleRegisteredComponentEvent}" phase="capture"></aura:handler> |
LightningEventsCompContainer.cmp
| <aura:handler name="totalIncomeComponentEvent" event="c:LightningComponentEvent" action="{!c.handleTotalIncomeComponentEvent}"></aura:handler> |
Now, you'll see that the order of output is as follows:-
Output 1-> Output 2-> Output 3. So, the source component handler runs in
capture phase and is executed first whereas the parent component runs in
bubble phase and is executed after that. Before discussing why this happened let's make one more change which is just reverse of this. Let's add
phase="capture" to
LightningEventsCompContainer.cmp this time and not to
LightningEventsComp1.cmp as shown below:-
LightningEventsComp1.cmp
| <aura:handler name="totalIncomeComponentEvent" event="c:LightningComponentEvent" action="{!c.handleRegisteredComponentEvent}"></aura:handler> |
LightningEventsCompContainer.cmp
| <aura:handler name="totalIncomeComponentEvent" event="c:LightningComponentEvent" action="{!c.handleTotalIncomeComponentEvent}" phase="capture"></aura:handler> |
Now, when you run the app again, you'll see that the order of output is as follows:-
Output 2-> Output 1-> Output 3. Make sure that you don't get confused by
Output 3 as it's the js delay in changing the value due to which other alert is executed as explained by me earlier. Just focus on
Output 1 and Output 2. So, we got to know that this time the handler in
LightningEventsCompContainer.cmp was in
capture phase and executed first whereas handler in
LightningEventsComp1.cmp was in
bubble phase and executed later.
So, from this, we can conclude that,
"If handlers of same event are in different phases, then the handlers in capture phase are executed first following their order where as the handlers in bubble phase are executed in their order after the handlers in capture phase have done their execution".
In other words we can say that
the capture phase is always handled before the bubble phase. But as we see, both the handlers are executed both the time. So, to have a better understanding, let's consider the previous code in which we have
LightningEventsCompContainer in
capture phase and
LightningEventsComp1 in
bubble phase. We are only going to do a single change now i.e. we are going to add
event.stopPropagation(); to our handler action which is running in
capture phase. So, below is the
LightningEventsCompContainerController.js :-
| ({ |
| // Function to handle Lightning Event |
| handleTotalIncomeComponentEvent : function(component, event, helper) { |
| alert('Event handler at the parent component'); |
| // Getting the value of totalIncome attribute of event using event.getParam() |
| var totalIncome = event.getParam('totalIncome'); |
| // Setting the totalIncome attribute of component with the event's totalIncome attribute value. |
| component.set('v.totalIncome', totalIncome); |
| event.stopPropagation(); |
| } |
| }) |
As you can see above, we have added
event.stopPropagation(); at the last line. Therefore, when you run the app now, you'll get the output in the following order:-
Output 2 -> Output 3. Notice that
Output 1 is not there. This is because, the capture phase stopped the event propagation and the event is not propagated in
any phase after that. But
what if we stop propagating the event in bubble phase ?? If you do so, you'll see that all the handlers that run in capture phase are executed successfully whereas only those handlers in the bubble phase are executed which come before the handler that stop the propagation of the event. I'll leave this to you as an exercise.
As a result, we can say that when an event propagation is stopped in bubble phase, it will
not propagate further only in
bubble phase as it has already propagated in capture phase before. Whereas, when an event propagation is stopped in capture phase, it'll not propagate further in
any phase as the bubble phase as it has already stopped propagating in capture phase and the bubble phase executes after that.