An In-Depth Look at Lightning Component Events

This post presents the specificities of Lightning component events (in opposition to application events). This event type is the most commonly used in the Lightning Component framework; yet, it’s also the most complex. Few Lightning developers know all of its “secrets.”

This article is a follow up of the Lightning Inter-Component Communication Patterns post. If you have not read it yet, read it first as it covers the basics of firing and handling events.

DOM event propagation basics

Before we get into the details of component events propagation, let’s first have a quick refresher on standard DOM event propagation. Forget the Lightning Component framework for a moment; we’re talking about standard HTML in this section.

DOM events follow two consecutive propagation phases that developers can follow: capture (rarely used) and bubble (widely used). Events first go down the DOM tree during the capture phase, then up during the bubble phase. The capture phase always executes before the bubble phase.

The diagram illustrates the two event propagation phases that occur along the DOM tree after a user clicks a button in a form. Notice that the event propagation does not originate from the button itself.

If you want to learn more on this subject, here’s a great article on DOM event propagation. But let’s get back to the Lightning Component framework for now.

Component event propagation

Handling an event in a specific phase

Lightning component events also support both capture and bubble phases, but there are some framework specificities in terms of syntax and behavior.

By default, component events are handled in the bubble phase, and that’s what most developers use.

To handle the capture phase, add a phase="capture" attribute to your event handler, like so:

<aura:handler name="cmpEvent" event="c:cmpEvent" action="{!c.handleCmpEvent}" phase="capture"/>

Note that you can declare two handlers for the same event if you wish to handle both phases (one for bubble and one for capture).

You can query the event’s current propagation phase by calling event.getPhase() in your event handling function. This function returns either capture or bubble.

Propagating to Container components

There’s a major difference between component events and DOM events: the way they propagate to containers.

To summarize, and as stated in the component event documentation:

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.

To put it simply: A Lightning component hierarchy does not behave like a DOM tree. By default (we get to the exceptions later), only parent components that create subcomponents (either in their markup or programmatically) can handle events. This singles out container components.

A container is a component that supports embedding subcomponents. In HTML, most elements are containers (for instance, a div or a span). In the Lightning Component framework, containers are components that expose one or more attribute of type Aura.Component[] such as the default body attribute.

For example, this code presents a containerCmp component:

<!-- c:containerCmp -->
<aura:component>
    {!v.body}
</aura:component>

containerCmp is a container since it exposes its body attribute in a {!v.body} expression. This implies that containerCmp can be used by a parent component parentCmp as shown here:

<!-- c:parentCmp -->
<aura:component>
    <c:containerCmp>
        <!-- some markup -->
    </c:containerCmp>
</aura:component>

By default, containers cannot handle events thrown by components they contain. That’s so because container components are not considered owners of events that they encapsulate.

Let’s extend the previous example by adding an eventEmitterCmp component embedded inside containerCmp:

<!-- c:parentCmp -->
<aura:component>
    <c:containerCmp>
        <c:eventEmitterCmp>
    </c:containerCmp>
</aura:component>

If eventEmitterCmp fires a component event, containerCmp cannot handle it by default, but parentCmp can.

If you want a container component to handle a component event, add an includeFacets="true" attribute to its handler, such as:

<!-- c:containerCmp -->
<aura:component>
    <aura:handler name="cmpEvent" event="c:cmpEvent" action="{!c.handleCmpEvent}" includeFacets="true"/>
    {!v.body}
</aura:component>

Stopping Event Propagation

Just like for standard DOM events, the propagation of component events can be stopped to prevent other components from handling an event.

Calling event.stopPropagation() stops the event propagation regardless of the current propagation phase.

As a best practice, do not stop event propagation unless you have a particular reason to do so. Leaving the event to propagate itself allows you to extend your application by adding event handlers in other components.

Pausing and Resuming Event Propagation

Unlike DOM events, component event propagation can be paused with event.pause() and resumed with event.resume(). The typical use case for this is asynchronous processing.

For example, the event handler’s code:

  1. Pauses a component event
  2. Calls a server-side action
  3. Then, the action callback resumes or stops the event propagation based on the server response
handleCmpEvent : function(component, event, helper) {
  // Pause event propagation
  event.pause();

  // Call an asynchronous server-side action
  var action = component.get("c.someServerFunction");
  action.setCallback(this, function(response) {
    var state = response.getState();
    if (state === "SUCCESS") {
      // Resume event propagation if action succeeded
      event.resume();
    }
    else if (state === "ERROR") {
      // Stop event propagation if action failed
      event.stopPropagation();
    }
  });
  $A.enqueueAction(action);
}

Closing words

You now know about the two event propagation phases: capture and bubble. You saw an overview of the subtleties of component events: event handling in container components and event propagation control functions (pausing, resuming, and stopping).

How about experiencing all of what you’ve learned in the interactive Lightning Component Playground App? Feel free to explore the different event functionalities and the source code.

Learn more on the Lightning Component framework by practicing with the Lightning Components Basics Trailhead module.

Leave your comments...

An In-Depth Look at Lightning Component Events