Custom Events in Angular

Custom Events in Angular 2

One of the main reasons for creating custom events is to provide and share information between different components in your application without having to directly linking them.

EventEmitter in Angular 2 provides a straightforward, out-of-the-box, easy way to notify parent components that something has changed in any of its child components via the @input and @output events property.

“The child component exposes an EventEmitter property, which emits events when something happens. The parent binds to that event property and reacts to those events. Custom events can be called anything you want, and you can pass any type of information you want.” See Component Communication for more details.

That’s great, but it cannot be used to communicate across a deeper component hierarchy. And this is one of the “big surprises” many developers encounter when starting to use Angular 2, because it’s not immediately clear that these events don’t bubble and will only be propagated and communicated within immediate child/parent components.

So one question remains: What’s the best solution to create custom events in Angular 2 that will allow communication between any components across the whole application?

It seems the common approach for enabling communication among non-siblings components is to use a shared service with an observable that can be injected into any component.

To demonstrate these scenarios, I have created a small application that shows how we can communicate between parent and child components OR any components.

Custom events with EventEmitter (Parent → Child Communication)

We have a generic class called “Family”. Any member of the “Family” or any component inherited from the family class will share its methods and properties.

One of the properties of the Family class is:

@Output() countUpdate = new EventEmitter();

So ChildComponent, ParentComponent, and GrandParentComponet all share the same “updater” property. The “updater” property returns an EventEmitter. Basically, the exact purpose of EventEmitters is to enable parent components to get data from child components. All @Outputs are EventEmitters, and the @Output decorator exposes an event that parents can attach listeners to in their templates.

You can then emit an event like this:

this.countUpdate.emit({ value: ++this.counter });

And the parent can listen for the event and attach a callback simply like this:

<child (countUpdate)="onUpdate($event)"></child>

This @Output mechanism makes (child → parent) communication as easy as the normal hierarchical data flow (parent → child).

In short, the data flow in Angular 2 follows these basic rules:

  1. To send information from parent to child, the parent binds to the child’s @Input properties in the template with [square brackets].
  2. To receive information from the child, the parent binds to the child’s @Output events with (parentheses).

Custom events with a shared service

To be able to communicate between components that are not directly “related” to each other, we need to create a shared service, which will act as the “middleman”.

In this example, our middleman is a shared service called “Broadcaster”.

Our Broadcaster is a really simple service that has only two methods: broadcast and on.

  • broadcast: is used to fire an event with an event-specific key.
  • on: is used to listen to an event-specific key and return an observable of events (Subject).

It is important to note that we are using a different type of Observable: Subject.

What is a Subject? “An RxJS Subject is a special type of Observable that allows values to be multicasted to many Observers. While plain Observables are unicast (each subscribed Observer owns an independent execution of the Observable), Subjects are multicast.” More info here: http://reactivex.io/rxjs/manual/overview.html#subject

And why is that important? It’s because a Subject is also a type of observable that can listen to (subscribe) or emit (next) events.

Back to our solution: you can now inject the Broadcaster service into any component. We want all the Family members to listen to the new “globalCountUpdate” global event, so we add this broadcaster into the Family class. By the way, you can call this custom event (‘globalCountUpdate’) anything you want.

import { EventEmitter, Output, OnDestroy } from '@angular/core'; import { Broadcaster } from './broadcaster'; export class Family { @Output() countUpdate = new EventEmitter(); constructor(private broadcaster: Broadcaster) { this.counter = 0; this.broadcaster.on(‘globalCountUpdate’) .subscribe(message => { this.counter++; }); } onUpdate(event) { this.counter = event.value; } onClick() { this.countUpdate.emit({ value: ++this.counter }); } }

You will note that this simple service is going to work similar to the “$rootScope.$broadcast” from Angular 1.x.

Ok! So all the components are able to listen to the event “‘globalCountUpdate,’” but how can we trigger that event?

It is really simple. All you need to do is import the Broadcaster service into the component and use the “.broadcast” method.

this.broadcaster.broadcast('globalCountUpdate', 'I am your friend!');

The first parameter is a string-based custom event name and the second parameter is optional data of any type you want (string, object, array, number, function, etc).

Conclusion

Angular 2 offers a straightforward and simple communication option—via @Inputs and @Outputs—between components that are directly related (Parent → Child); however, it’s not really clear what the de facto solution is when we want to communicate or share information between no directly related components.

In this article I have presented a simple yet powerful solution using a shared service that works similar to the former Angular 1.x $broadcast method and allows you to create custom events for communication between any components.

Other resources and examples

 

Please check the live code and example here:
https://embed.plnkr.co/bcI4OXFrZ93CeEM8jaq9/

Component Interaction – Angular 2 Official Documentation
https://angular.io/docs/ts/latest/cookbook/component-communication.html

RXJS Subject
http://reactivex.io/rxjs/manual/overview.html#subject