Cover photo by Debby Hudson on Unsplash
By reviewing Angular code daily through mentoring or community support, I happen to find EventEmitters being used in Angular services.
Here is an example with a service dedicated to broadcasting some data to other parts of the application :
export class DataService {
data : EventEmitter < Data > = new EventEmitter < Data >();
updateData ( data : Data ) : void {
By using dependency injection, a component can subscribe to the EventEmitter to receive the emitted values :
export class MyComponent {
constructor ( private readonly dataService : DataService ) {}
this . dataService . data . subscribe ( () => {
It works: if another part of the application uses updateData
to emit a value, the component will receive it.
So why shouldn’t you use it?
Let’s take a look at EventEmitter API.
Here is a simplified version of the original EventEmitter codebase based on its usage in the previous code samples :
class EventEmitter extends Subject < any > {
subscribe ( observerOrNext ?: any , error ?: any , complete ?: any ) : Subscription {
const sink = super . subscribe ( {next: observerOrNext , error: error , complete: complete } );
From creating your *EventEmitter* to the subscription, you use nothing more than the extended class : a *Subject*.
The first motivation not to use an *EventEmitter* over a *Subject* is to *keep it simple stupid* (KISS), as a Subject already provides you all you need.
The second reason lies in the original purpose of an *EventEmitter* as explained in the [reference API](https://angular.io/api/core/EventEmitter) :
> Use in components with the @Output directive to emit custom events synchronously or asynchronously, and register handlers for those events by subscribing to an instance.
By using it for another purpose **might lead to bugs** if changes occur on this API for the benefit of its original purpose.
## How to refactor your codebase
A reminder of the previous usage:
export class DataService {
data : EventEmitter < Data > = new EventEmitter < Data >();
updateData ( data : Data ) : void {
The required changes are:
- the type from EventEmitter to Subject
- the exposure of the Subject as a private resource for the service to avoid external emissions
- the creation of a public Observable version of the Subject you can subscribe to
- an update to match the API to emit a new value
export class DataService {
// change the type and the visibility
private dataSubject : Subject < Data > = new Subject < Data >();
// create a public observable out of the subject for external usage
data$ : Observable < Data > = this . dataSubject . asObservable ();
updateData ( data : Data ) : void {
this . dataSubject . next (data);
Such a move is also a great opportunity to explore [variant Subjects](https://rxjs.dev/guide/subject): BehaviorSubject, ReplaySubject, and AsynSubject.