Home > Software engineering >  what is the proper way to have multiple components subscribe to an observable?
what is the proper way to have multiple components subscribe to an observable?

Time:11-09

I have a client service, with a method:

get (params) {
  return this.http.get(url, { params: params });
}

and then a service that performs this:

fetch () {
  return this.client.get(this.params);
}

and then a component that makes use of that service:

# in template
<a (click)="clickHandler()">click me</a>
<some-other-component></some-other-component>

# method in component class
clickHandler () {
  this.service.fetch();
}

My question is, if I want SomeOtherComponent to be able to do something when this api call is completed, what is the best way to handle that so that it can subscribe to the get call?

I know I could do something like:

# in template
<some-other-component [observable]="observable"></some-other-component>

# method in component class
clickHandler () {
  this.observable = this.service.fetch();
}

And say within that SomeOtherComponent are other child components that each want to make use of the results of that web call... Would passing the observable directly like this be the best way to go? So each of them call .subscribe(first()).pipe(...) and do what they need to with it?

CodePudding user response:

I am assuming you have a few components living in a template HTML and you want to provide the observable's value to all of them, the proper way would be to subscribe to this observable using the async pipe and then just pass the raw data to the children components:

...
theObservable$!: Observable<string>;

...
clickHandler(): void {
  this.theObservable$ = this.service.fetch();
}
<button (click)="clickHandler()">Fetch</button>

<ng-container *ngIf="theObservable$ | async as myValue"> <!--you subscribe once-->
 <component-one [aValue]="myValue"></component-one>
 <component-two [someData]="myValue"></component-two>
 <component-three [moreValues]="myValue"></component-three>
</ng-container>

Also, let's say you have a bunch of observables that you need to provide to your components, you could do it like this:

users$!: Observable<User[]>;
articles$!: Observables<Article[]>;
total$: Observable<number>;

ngOnInit(): void {
  this.users$ = this.userService.getUsers();
  this.articles$ = this.articleService.getArticles();
  this.total$ = this.service.getTotal();
}
<ng-container *ngIf="{
  users: users$ | async,
  articles: articles$ | async,
  total: total$ | async
} as localData">
  <app-users [userList]="localData.users"></app-users>
  <app-detail 
   [articles]="localData.articles"
   [total]="localData.total" >
  </app-detail>
</ng-container>

CodePudding user response:

The other answer is a good way to do it but you can also implement your service to keep the data in a BehaviorSubject

Service

private data: BehaviorSubject<any> = new BehaviorSubject();
data$ Observable<any> = this.data.asObservable();

fetch(): void {
    yourcall.subscribe((yourdatacomingback) => 
        this.data.next(yourdatacomingback))
}

component

ngOnInit(): void {
    service.data.subscribe((datafromservice) => {
        this is gonna be called everytime the data updates in the service as long as 
        the component hasnt been destroyed so you can do what you want with the result 
    })
}

So whats happening is when you call fetch, the response comes back and call next on the behaviorsubject. When next is called its going to trigger the subscribe in the components that are listening to it.

  • Related