You shouldn't use EventEmitters in Angular services

Gérôme Grignon - Jan 6 '22 - - Dev Community

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 {
    this.data.emit(data);
  }
}
Enter fullscreen mode Exit fullscreen mode

By using dependency injection, a component can subscribe to the EventEmitter to receive the emitted values :


export class MyComponent {
  constructor(private readonly dataService: DataService) {}

  ngOnInit() {
    this.dataService.data.subscribe(() => {
      // do whatever you want
    });
  }
}

Enter fullscreen mode Exit fullscreen mode

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> {
  constructor() {
    super();
  }

  emit(value?: any) {
    super.next(value);
  }

  subscribe(observerOrNext?: any, error?: any, complete?: any): Subscription {
    const sink = super.subscribe({next: observerOrNext, error: error, complete: complete});

    return sink;
  }
}
```



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:



```typescript
export class DataService {
  data: EventEmitter<Data> = new EventEmitter<Data>();

  updateData(data: Data): void {
    this.data.emit(data);
  }
}
```



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



```typescript
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 {
    // update the API
    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.

Happy coding!
Enter fullscreen mode Exit fullscreen mode
. . . . . . . . . .
Terabox Video Player