0
votes

We try to use BehaviorSubject to share API data across multiple components. On my component I am triggering a HTTP request and updating the subject once the response is returned.

component.ts

onClick() {
    this.service.getCompanies();
    this.service.companiesList$.subscribe(companies => {
        console.log(companies.length); // 0 and then 1
    });
}

service.ts

companiesList$ = new BehaviorSubject([]);

getCompanies() {
    return this.http.get(myUrl).subscribe((res: any) => {
        this.companiesList$.next(res.data);
    });
}

How can I access only the last emitted value from the BehaviourSubject once the request is finished?

4
As some of the replies have implied, you don't need to wrap the Observable provided from an HTTP call in another Observable (the BehaviorSubject). You already have an Observable you can work with. Consider trying a declarative approach to your Observables. That will make notifications easier. See this for more information: youtube.com/watch?v=Z76QlSpYcck - DeborahK

4 Answers

1
votes

Maybe the you could try the following, save the observable of the http request in the service and apply a shareReplay(1) operator on it. Then in your component(s) you can use it like the following:

component.ts

onClick() {
    this.service.getCompanies().subscribe(companies => {
        console.log(companies);
    });
}

service.ts

$companies =  this.http.get(myUrl).pipe(shareReplay(1));

getCompanies() {
    this.companies$;
}
0
votes

While technically you could use companiesList$.getValue(), I'd recommend not to create a subscription in your component using subscribe(), and use AsyncPipe instead (ref). It could look like that:

component.ts

companies$ = this.service.companiesList$.asObservable();

onClick() {
  this.service.getCompanies();
}

component.html

<div *ngIf="companies$ | async as companies">
  <!-- your stuff -->
  {{ companies.length }}
</div>
0
votes

To get the last emit value use:

companiesList$.getValue().

If you what to use it within the stream use the withLatestFrom operator

0
votes

I think what the answers here are missing is that you are using a BehaviourSubject, which is the wrong type of subject.

In short :

  • A Subject will output values only from the time you subscribe. So if emit is called on a Subject 5 times, then a component subscribes, they won't see those previous 5 emits.

  • A ReplaySubject will output X amount of values immediately when you subscribe. So if I have a ReplaySubject(1), and I output a value 5 times, then a component subscribes. They will immediately receive the last 1 value, then they will continue to receive further emits.

  • A BehaviourSubject is almost identical to a ReplaySubject except it has a default value that will always be emitted, regardless of whether you have called emit on the subject.

In your code you have :

companiesList$ = new BehaviorSubject([]);

So even before you call emit, anyone who subscribes to this will immediately receive an empty array. So I think reading between the lines of your question, you are having issues receiving an empty array when you subscribe, then you are receiving your actual data.

You can resolve this by using a ReplaySubject :

companiesList$ = new ReplaySubject(1);

Which will output nothing until the first emit, then any subscribes will always receive this "last" emit.

It's worth learning the differences of each type of Subject here : https://tutorialsforangular.com/2020/12/12/subject-vs-replaysubject-vs-behaviorsubject/