简体   繁体   中英

private Subject vs. public readonly Subject in Shared Services

I have started working on an angular 8 project where two sibling components have to exchange data. So far, the approach was to have an EventEmitter in the parent's service. The child components then called the emit methods on these emitters to pass the data to the other sibling. Here an example case:

Shared Service: (bad)

@Injectable()
export class DocumentViewerService {
    public readonly annotationDeletedInAnnotationPanel = new EventEmitter<string>();

Child Component: (bad)

this.documentViewerService.annotationDeletedInAnnotationPanel.emit(annotationId);

However, I found several online sources ( 1 , 2 ) that claim using EventEmitters in Services is bad practice.

Instead, most online sources ( 3 , 4 ) propose a pattern using Subjects and shared services for sibling communication.

Shared Service: (good)

@Injectable()
export class DocumentViewerService {
   private deletedAnnotationInAnnotationPanel$ = new Subject<string>();
   public readonly deletedAnnotationInPanel$ = this.deletedAnnotationInAnnotationPanel$.asObservable();

   deleteAnnotationInPanel(annotationId: string) {
        this.deletedAnnotationInWebViewer$.next(annotationId);
   }

Child Component: (good)

this.documentViewerService.deleteAnnotationInPanel(id);

Both approaches leave me with a few questions:

  1. Why is it bad practice to use EventEmitters in Service . You don't need to put the @Output tag and it will act similar to an observable when called with emit in one of the childs.
  2. What's the reason to make the Subject private in the supposedly better approach? Children components can still change the values by calling the provided method deleteAnnotationInPanel (in sample above).
  3. What's the difference in making the Subject deletedAnnotationInAnnotationPanel$ private vs. public readonly . The latter would allow everyone to subscribe and call .next, but not to change initialisation. Instead of five lines of code, I would only have one:

    public readonly deletedAnnotationInPanel$ = new Subject();

None of the online sources seem to explain why to make the Subject private with a setter method and an additional public observable for children.

Hopefully, you can provide me with some strong arguments or insights into why one should be preferred over the others. Thanks in advance for the support!

  1. Why is it bad practice to use EventEmitters in Service.

    EventEmitter is meant to be used in Components. As the documentation says,

    Use in components with the @Output directive to emit custom events synchronously or asynchronously...

    Even though it has some semantics of Subject, its not designed for that purpose.

  2. What's the reason to make the Subject private in the supposedly better approach?

    Subject is like state of your Service class, you don't want to expose it publicly and have rest of the application start triggering events out of it. You want to ensure that service emits events only when observers needs to be informed for real. Thus, by writing methods like deleteAnnotationInPanel , you are building a layer of abstraction, and ensuring that users of service perform actions using service and some of those actions may result in events to be fired.

  3. What's the difference in making the Subject deletedAnnotationInAnnotationPanel$ private vs. public readonly.

    If you make it private , users of service class will not have access to it and no one will be able to subscribe to events being published by subject. Thus, making it public readonly is inevitable. You want it to be readonly because you don't want a rogue code to overwrite it to be observer for some other subject and start emitting events unnecessarily.

  1. Why is it bad practice to use EventEmitters in Service. You don't need to put the @Output tag and it will act similar to an observable when called with emit in one of the childs.

The Angular team don't guarantee that EventEmitter will always act like an observable, and using it as an observable is discouraged. EventEmitters are an abstraction of user events, don't have any place in services that are meant to be isolated from the UI.

  1. What's the reason to make the Subject private in the supposedly better approach? Children components can still change the values by calling the provided method deleteAnnotationInPanel (in sample above).

This is all about creating your own interface for how the different areas of your app interact. When you create your own service API, you are in control of it, and can make changes to the implementation without changing your interface.

  1. What's the difference in making the Subject deletedAnnotationInAnnotationPanel$ private vs. public readonly. The latter would allow everyone to subscribe and call .next, but not to change initialisation. Instead of five lines of code, I would only have one:

Exposing the raw subject means that any component can complete the subject and spoil the party for everybody else. That's pretty dangerous. Expose only the behaviour that is needed to maintain control.

A few more lines of code that are much more maintainable are far better than fewer lines of code that have the potential to cause chaos in an unconstrained environment.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM