Event Bubbling and Angular 2

Checkout Radzen Demos and download latest Radzen!

Angular components and HTML elements communicate with the outside world through events. The Angular event binding syntax is the same for components and elements - the target event within parentheses, equal sign and a template statement.

<button (click)="onClick()">Click</button>

HTML Elements

Some of the events, that HTML elements fire, propagate through the DOM hierarchy. This propagation process is called event bubbling. Events are first handled by the innermost element and then propagate to the outer elements until they reach the root.

DOM event bubbling works seamlessly with Angular (plunkr).

@Component({
  selector: 'event-bubbling',
  template: `
    <div (click)="onClick()">
      <button>Click</button>
    </div>
  `
})
export class EventBubblingComponent {
  onClick() {
    alert('Click');
  }
}

Clicking the button displays the message even though the parent element handles the event.

Angular Components

Angular provides support for custom events via Output properties and the EventEmitter. Unlike DOM events Angular custom events do not bubble. Trying to bind to a custom event of a child component will lead to unknown directive error.

Binding to a DOM event triggered by a child element works as expected (plunkr).

@Component({
  selector: 'event-bubbling',
  template: `
    <div>
      <button>Click</button>
    </div>
  `
})
export class EventBubblingComponent {
}

@Component({
  selector: 'my-app',
  template: `
    <div>
      <event-bubbling (click)="onClick()"></event-bubbling>
    </div>
  `,
})
export class App {
  onClick() {
    alert('Click!');
  }
}

Custom Events Named After DOM Events

What if EventBubblingComponent emitted a custom event called click? Let’s try that (plunkr).

@Component({
  selector: 'event-bubbling',
  template: `
    <div>
      <button (click)="onClick('Button 1')">Button 1</button>
      <button (click)="onClick('Button 2')">Button 2</button>
    </div>
  `
})
export class EventBubblingComponent {
  @Output() click = new EventEmitter();

  onClick(button: string) {
    this.click.next(button);
  }
}

@Component({
  selector: 'my-app',
  template: `
    <div>
     <event-bubbling (click)="onClick($event)"></event-bubbling>
    </div>
  `,
})
export class App {
  onClick(button: string) {
    alert(button);
  }
}

Clicking a button displays two messages now - first the button title (as expected) and then [Object object] (or [Object MouseEvent]). Where does the second alert come from? In this case Angular handles both the custom and DOM click events. The DOM event handler executes second and receives a DOM Event object as a parameter hence alert shows [Object object].

How to fix that? DOM events provide a mechanism that can prevent bubbling. It is the stopPropagation method. Here is how to use it (plunkr).

@Component({
  selector: 'event-bubbling',
  template: `
    <div>
      <button (click)="onClick($event, 'Button 1')">Button 1</button>
      <button (click)="onClick($event, 'Button 2')">Button 2</button>
    </div>
  `
})
export class EventBubblingComponent {
  @Output() click = new EventEmitter();

  onClick(event: Event, button: string) {
    event.stopPropagation();

    this.click.next(button);
  }
}

The problem is now fixed. Clicking a button displays only one message.

Takeaway

Event bubbling allows a single handler on a parent element to listen to events fired by any of its children.

Angular supports bubbling of DOM events and does not support bubbling of custom events.

Mixing the names of custom and DOM events (click, change, select, submit) could be problematic. Use stopPropagation to avoid double event handler invocation. Prefer naming custom events after their intent and not cause.

<!-- custom event clashes with DOM event -->
<product-form (submit)="onSubmit($event)"></product-form>
<!-- custom event shows its intent and does not clash -->
<product-form (save)="onSaveProduct($event)"></product-form>

Leverage Radzen on LinkedIn

Yes, we are on LinkedIn and you should follow us!

Now, you have the opportunity to showcase your expertise by adding Radzen Blazor Studio and Radzen Blazor Components as skills to your LinkedIn profile. Present your skills to employers, collaborators, and clients.

All you have to do is go to our Products page on LinkedIn and click the + Add as skill button.