I am trying to study and understand component event propagation from here - https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/events_component_bubbling.htm
The theory is understandable, but when I implemented the 'Event Bubbling Example' given on the same URL, but I am having trouble understanding a few points.
When I execute the example AS IT IT given on that link, this is the output -
Grandchild handler for bubblingEvent
Parent handler for bubblingEvent
The parent handler being executed here makes perfect sense, as the parent is at the top of the containment hierarchy.
Question 1 -
According to the 'Default Event Propagation Rules' section on the same page-
<!--c:owner-->
<aura:component>
<c:container>
<c:eventSource />
</c:container>
</aura:component>
By default, every parent in the containment hierarchy can’t handle an event during the capture and bubble phases. Instead, the event propagates to every owner in the containment hierarchy.
A component’s owner is the component that is responsible for its creation. For declaratively created components, the owner is the outermost component containing the markup that references the component firing the event. For programmatically created components, the owner component is the component that invoked $A.createComponent to create it.
If c:eventSource fires an event, it can handle the event itself. The event then bubbles up the containment hierarchy.
c:container contains c:eventSource but it’s not the owner because it’s not the outermost component in the markup, so it can’t handle the bubbled event. c:owner is the owner because c:container is in its markup. c:owner can handle the event.
Going by this logic, why is eventBubblingGrandChild component allowed to handle the event fired by eventEmitter, as it is not the outermost component in the markup? It, however, is the component that directly instantiated eventEmitter.
Question 2 -
This is the original markup for eventBubblingChild
.
<aura:component>
<aura:handler name="bubblingEvent" event="c:compEvent" action="{!c.handleBubbling}"/>
<div class="child">
{!v.body}
</div>
</aura:component>
{!v.body}
is used here as the example intends to pass eventBubblingGrandChild
as the body of eventBubblingChild
inside eventBubblingParent
.
But when I decided to change the markup to something like this -
eventBubblingChild -
<aura:component>
<aura:handler name="bubblingEvent" event="c:compEvent" action="{!c.handleBubbling}" />
<div class="child">
<c:eventBubblingGrandChild />
</div>
</aura:component>
eventBubblingParent -
<aura:component>
<aura:handler name="bubblingEvent" event="c:compEvent" action="{!c.handleBubbling}" />
<div class="parent">
<c:eventBubblingChild />
</div>
</aura:component>
I see this output -
Grandchild handler for bubblingEvent
Child handler for bubblingEvent
Parent handler for bubblingEvent
What can be a possible explanation for this output? How is it possible that upon replacing {!v.body}
with explicit component instantiation, eventBubblingChild
is able to handle the event? Please note that includeFacets="true" is not added to any aura:handler tag. (without which a container component cannot handle an event)
Question 3 -
I decided to experiment with the the capture phase handlers as well. I changed the markup to this -
eventBubblingGrandChild -
<aura:component>
<aura:handler name="bubblingEvent" event="c:compEvent" action="{!c.handleBubbling}" phase="bubble" />
<aura:handler name="bubblingEvent" event="c:compEvent" action="{!c.handleCapture}" phase="capture" />
<div class="grandchild">
<c:eventBubblingEmitter />
</div>
</aura:component>
eventBubblingGrandChildController.js -
({
handleBubbling: function (component, event, helper) {
console.log("Bubble Phase. Grandchild handler for " + event.getName());
},
handleCapture: function (component, event, helper) {
console.log("Capture Phase. Grandchild handler for " + event.getName());
}
})
eventBubblingChild -
<aura:component>
<aura:handler name="bubblingEvent" event="c:compEvent" action="{!c.handleBubbling}" phase="bubble" />
<aura:handler name="bubblingEvent" event="c:compEvent" action="{!c.handleCapture}" phase="capture" />
<div class="child">
<c:eventBubblingGrandChild />
</div>
</aura:component>
eventBubblingChildController.js -
({
handleBubbling: function (component, event, helper) {
console.log("Bubble Phase. Child handler for " + event.getName());
},
handleCapture: function (component, event, helper) {
console.log("Capture Phase. Child handler for " + event.getName());
}
})
eventBubblingParent -
<aura:component>
<aura:handler name="bubblingEvent" event="c:compEvent" action="{!c.handleBubbling}" phase="bubble" />
<aura:handler name="bubblingEvent" event="c:compEvent" action="{!c.handleCapture}" phase="capture" />
<div class="parent">
<c:eventBubblingChild />
</div>
</aura:component>
eventBubblingParentController.js -
({
handleBubbling: function (component, event, helper) {
console.log("Bubble Phase. Parent handler for " + event.getName());
},
handleCapture: function (component, event, helper) {
console.log("Capture Phase. Parent handler for " + event.getName());
}
})
Here is the output -
Capture Phase. Parent handler for bubblingEvent
Capture Phase. Child handler for bubblingEvent
Capture Phase. Grandchild handler for bubblingEvent
Bubble Phase. Grandchild handler for bubblingEvent
Bubble Phase. Child handler for bubblingEvent
Bubble Phase. Parent handler for bubblingEvent
This output makes sense in a way, because capture phase goes from application root to source, and bubble phase goes from source component to application root. But mostly the 2nd question holds here as well. Why are the handlers for child component getting called, without includeFacets="true" being included anywhere?
Hope to understand these concepts soon enough. Let me know if more information is required.