0

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.

2

As you've quoted from the documentation, changing {!v.body} to <c:eventBubblingChild /> causes the owner of the component to change.

<aura:component>
  <aura:handler name="emitter" event="c:iAmAnEmitterEvent" action="{!c.whatever}" />
  <c:iAmAFacet>
   <c:iAmAlsoAFacet>
     <c:iAmAnEmitter>
   </c:iAmAlsoAFacet>
  </c:iAmAFacet>
</aura:component>

In this example, an event from <c:iAmAnEmitter> can only be handled by the handler at the top, because everything was created by the top-level component; it "owns" everything inside of it.

As soon as you move things to a child:

<aura:component>
  <aura:handler name="emitter" event="c:iAmAnEmitterEvent" action="{!c.whatever}" />
  <c:iAmAFacet>
   <c:iAmAParent />
  </c:iAmAFacet>
</aura:component>

iAmAParent

<aura:component>
  <aura:handler name="emitter" event="c:iAmAnEmitterEvent" action="{!c.whatever}" />    
  <c:iAmAnEmitter />
</aura:component>

Now, there are two owners. Both iAmAParent and the top-level component are in the owner hierarchy, so both can respond to the event.

Next, let's look at dynamic creation:

<aura:component>
  <aura:handler name="emitter" type="c:iAmAnEmitterEvent" action="{!c.whatever}" />
  <c:iAmAChild />
</aura:component>

iAmAChild

<aura:component>
  <aura:handler name="emitter" type="c:iAmAnEmitterEvent" action="{!c.whatever}" />
  {!v.body}
</aura:component>

$A.createComponents(
[['c:iAmAnEmitter',{}]],
function(cmp) { component.set("v.body", cmp); }
);

In this case, iAmAChild created the component dynamically, so it owns iAmAnEmitter. It can respond to the event, as can the parent component.

The keys here are:

  • Whichever component creates another component is the owner of that component.
  • Only owners are allowed to respond to events by default.

Every component has exactly one owner. The owner hierarchy is always allowed to respond to an event. The owner is always the component where the component was defined (in markup) or created (in JS). Facets are any component between the owner and the eventual location of the child component, typically traversed through {!v.body} or any other aura:attribute type="Aura.Component[]" chain.

To demonstrate the last example:

<aura:component>
  <aura:handler name="emitter" type="c:iAmAnEmitterEvent" action="{!c.whatever}" />
  <c:parent>
   <c:child>
     <aura:set attribute="grandchild">
       <c:grandchild />
     </aura:set>
   </c:child>
  <c:/parent>
</aura:component>

child

<aura:component>
  <aura:attribute name="grandchild" type="Aura.Component[]" />
  {!v.grandchild}
</aura:component>

grandchild

<aura:component>
  <aura:attribute name="emitter" type="c:iAmAnEmitterEvent" action="{!c.whatever}" />
  <c:iAmAnEmitter />
</aura:component>

In this last example, the top-level component owns the grandchild; the child component can't handle the event by default, because it does not own grandchild.

The worst thing you can do is try to over-complicate or over-think the situation. It's actually really, incredibly simple. Whichever component defines or creates the child component owns that component, and can respond to events that propagate through the hierarchy, and no other component can do so by default.

To give an extreme example, consider:

<aura:component>
    <c:cmp1>
        <c:cmp2>
            <c:cmp3>
                <c:cmp4>
                    <c:cmp5>
                        <c:cmp6>
                            <c:emitter />
                        </c:cmp6>
                    </c:cmp5>
                </c:cmp4>
            </c:cmp3>
        </c:cmp2>
    </c:cmp1>
</aura:component>

In this example, cmp1, cmp2, cmp3, cmp4, cmp5, and cmp6 are all facets, and cannot handle events from emitter by default. The top-level component owns all seven components, and can respond to events from any of them, but none of the seven components can respond to events from any other component by default. There is exactly one "owner" in this component graph.

2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.