I was using EventEmitter
and @Output
in Angular services, today one of the colleagues mentioned it's not a good practice.
I found this post mentioning it's a bad practice and it seems mostly is personal opinion, and this answer is mentioning it's OK to use it. I couldn't find any official document about it, so if somebody knows an official answer for it please post.
Official doc about EventEmittter
I was using EventEmitter
and @Output
in Angular services, today one of the colleagues mentioned it's not a good practice.
I found this post mentioning it's a bad practice and it seems mostly is personal opinion, and this answer is mentioning it's OK to use it. I couldn't find any official document about it, so if somebody knows an official answer for it please post.
Official doc about EventEmittter
Share Improve this question edited Aug 22, 2022 at 9:47 jaibalaji 3,4752 gold badges16 silver badges29 bronze badges asked Jun 1, 2018 at 16:32 RezaReza 19.9k16 gold badges98 silver badges173 bronze badges 5-
In an Angular serivce, you're better off just using a
subject
I would've thought. Certainly I can't see even benefit in adding the@Output
within a service – user184994 Commented Jun 1, 2018 at 16:34 - Inputs and Outputs should be used for binding to attributes/events of ponents (targeting them from the HTML), NOT in a service. Like mentioned above, it would be better to have a subject or an observable that can be retrieved through a get function or something. – MichaelSolati Commented Jun 1, 2018 at 16:48
- I was wondering why my question gets down votes and close votes, and almost same question got 132 up votes stackoverflow./questions/36076700/… – Reza Commented Jun 1, 2018 at 16:50
- It's unmon as plain Subjects are preferred. EventEmitter is an enhanced Subject, so it works, but why plicate things? – bc1105 Commented Jun 1, 2018 at 17:00
-
@bc1105 thanks, using
@output
is easier and more neat paring toBehaviorSubject
andSubject
– Reza Commented Jun 1, 2018 at 17:03
2 Answers
Reset to default 11I was using EventEmitter and @Output in Angular services, today one of the coleagues mentioned it's not a good practice.
The annotation @Output()
has no effect in a service. It is used to tell the Angular template piler to bind an Observable
to a template expression.
If I saw @Output()
in a service, then I'd tell the developer to remove it.
EventEmitter
is an Observable
and there are no side effects in using it in a service, but there are also no benefits.
You can use any Observable
type of emitter in either a ponent or service. There are two reasons why we have EventEmitter
. 1) it pre-dates the Angular teams decision to mit to using observables and they thought they might need their own implementation, 2) it can emit values in the next JavaScript cycle (optional setting).
There were edge cases were people needed to emit changes in the next cycle to avoid problems with change detection.
Secure Your Observables
@Injectable()
export class MyService {
public events: Subject<any> = new Subject();
}
The problem with the above service is that anyone can emit values from the public events
. You want your service to be the only code that handles emitting values.
@Injectable()
export class MyService {
private _events: Subject<any> = new Subject();
public get events(): Observable<any> {
return this._event.asObservable();
}
}
The above is better because access to the Subject.next(..)
is private. Consumers can only subscribe to the observable.
If you followed the ponents approach. It forces you to expose your emitter which isn't a good idea.
@Injectable()
export class MyService {
@Output() // <<< has no effect
public events: EventEmitter<any> = new EventEmitter();
// ^^ makes the emitter public
}
Components need to have their properties as public if they are to be used in templates, but this isn't the case for services.
As of today I realized having EventEmitter at the service level is rather bad because when subscribing to to the event handler in ngInit as I keep switching pages my subscriptions accumulate and one time emit() is handled X time subscribed.
export class XService {
@Output() test: EventEmitter<number> = new EventEmitter<number>();
public parentInitCount : number = 0;
public childInitCount : number = 0;
}
Parent ponent
constructor(private events: XService) { }
public ngInitCounter: number = 0;
ngOnInit() {
this.ngInitCounter++;
this.events.parentInitCount++;
}
runJob() {
this.events.test.emit(this.ngInitCounter);
}
Child
constructor(private events: XService) { }
public ngInitCounter: number = 0;
ngOnInit() {
this.events.test.subscribe((value)=> { //subscriptions will accumulate going back and forth between pages, because service keeps state
console.log('test event, parent ngInitCount: ' + value + ' child ngInitCount: ' + this.ngInitCount);
console.log('test event2, service ngInitCount: ' + this.events.parentInitCount + ' service child ngInitCount: ' + this.events.childInitCount);
});
this.ngInitCount++;
this.events.childInitCount++;
}