Observables - Unsubscribing httpClient required?

Hi all, with events being deprecated I am working through the use of observables. Taking some inspiration from this post I have implemented an observable to retrieve contacts via an HTTP service.

httpService.ts

getContactsHttp(params: any) {
     let headers: any = new HttpHeaders(this.authService.getHeadersClient());
     return this.httpClient.get<ContactSearch[]>('/contacts', {headers: headers, params: params});
    }

I then have a contact service which makes the actual calls and stores the response in a Behaviour Subject contacts$ .

contactService.ts

  contacts$ = new BehaviorSubject<ContactSearch[] | null>(null);
  refreshContacts(params: any) {
    this.httpService.getContactsHttp(params).subscribe((data:ContactSearch[])=>{
      this.contacts$.next(data);
    });  
  }

directory.ts
In my component I retrieve contacts on init.

contacts$: Observable<ContactSearch[]>;
constructor() {
 this.contacts$ = this.contactService.contacts$;
}
ngOnInit(){
 this.contactService.refreshContacts("");
}

I also have another component which updates a contact then calls refreshContacts which then refreshes contacts in the directory component.

Based on this I have 2 questions:

Question 1
In refreshContacts() do I need to unsubscribe from the http call?

Question 2
Is there are better way to do refreshContacts in the contact service? ie rather than subscribing are there alternate ways to emit next value when when http response is received?

Those are two very insightful questions, and it was also extremely helpful that you included the final comment about the other component.

Answer 1

No, but the only reason is that ContactService lives for the entire life of the app run. If it were a transient object like a page or component, then the answer would be yes, and the method I use and would recommend is ngx-take-until-destroy. So, if you decide to subscribe in DirectoryComponent, I would use that operator. I have found that in most cases, life is easier if I do that, because I’m often wanting to do stuff in the controller instead of in the template with the AsyncPipe.

Answer 2

Given the design you’ve described (especially the fact that the other component that triggers updates also goes through refreshContacts), it would seem that refreshContacts is a single point of update. IOW, if any other code path in the app manages to call getContactsHttp aside from refreshContacts, that’s a bug. If that’s true, then I think the way you have done things is best and simplest.

If refreshContacts is not the only way contacts can get updated, then yes, there is an alternate approach which is harder to read and more complex: making a vampire HttpInterceptor that listens for HTTP requests to that particular endpoint and tells ContactService to update. In effect, you’d be farming out part of what is now ContactService's work to that interceptor. I don’t really like this design for that reason, because it diffuses work that I think really belongs in one object, but if there is a compelling reason to do so, it’s an option.

In summary, carry on: the main thing I would change here is giving refreshContacts's params a proper type instead of resorting to any.

1 Like

Thanks for the response.

helpful that you included the final comment about the other component.

Yes I probably should have made it more explicit but glad you picked up on that. If it was just the one call from the component then I would have the observable purely in the component.

Re: Answer 1, cheers noted and makes sense. Will also check on the plugin.

Re: Answer 2, ok great. My main concern was having a ‘susbcribe’ within a service rather than the component. But for applying next() I did not know another way and from you have mentioned this method is ok. Noted on HttpInterceptor, I’m slowly grasping the power of it (including cutting down on my error handling methods considerably which used to be in my http services for each API).

Thanks again!

FWIW, I still wouldn’t. I really strongly believe in putting all business-level data wrangling in services, not in components. In my apps, components and pages only ever see data as Observable<Widget>, with nary a clue nor care from where that data came. The services are responsible from caching it, fetching it from storage, retrieving it from the network. I think this improves readability and futureproofing.

1 Like

Interesting point and makes sense. So just say I want to do an alphabetical sort on the data retrieved (eg using lodash). Where would you do this, in the component or service within subscribe?

On the server, but since that wasn’t given as an option, in the service, unless that component is the only one that wants it sorted that way.