Reputation: 159
I'm fairly new to the Angular scene and I'm starting to pull my hair out of over some unexpected behaviour.
I have the following code in my IntegrationsService
queryList(): Observable<ApiList[]> {
if(this.loadingApiList == true) {
return Observable.of(this.queryListResult);
}
this.loadingApiList = true;
return this._http
.get('samples/apiList.json').map( (response: Response) => <ApiList[]> response.json())
.do(data => {
this.queryListResult = data;
});
}
How can I stop this call being executed twice on page load from a route?
A component calls this method and builds a list in the navigation, also another component calls the same service for the following method:
getApiName(IT_ID:number) {
console.log(this.queryListResult);
let obj = this.queryListResult.find(o => o.IT_ID == IT_ID);
if(obj) {
return obj.IT_Name;
}
return false;
}
This works perfectly when I am not using routes, however when I use routes the getApiName returns an undefined result.
I used the resolve object too and that did resolve the problem but then two calls were being made to the json file, this is what I used.
resolve(route: ActivatedRouteSnapshot, state:RouterStateSnapshot):Observable<any>{
return this._service.queryList() // We need this to finish first.
}
I'm really stuck now - I can get it working using the resolve but it's wasteful having two calls to the .json.
What is the correct way of doing this?
Upvotes: 0
Views: 2267
Reputation: 19764
How can I stop this call being executed twice on page load from a route?
This is happening because you subscribe twice. Observables are lazy and each time you invoke one with .subscribe()
, it will start executing: in your case, it will trigger a network request. Think of .subscribe()
in observables as .call()
or simply ()
for functions: every time you call a function, it will execute.
With that in mind, the behavior you encounter is expected. There are different ways to solve this, which are similar yet slightly differ based on your use-case; ie. the exact timing of subscribing and unsubscribing. In general, what you're looking for is called multicasting.
The simplest form of multicasting is using the .shared()
operator. From the official docs; but note the emphasis (mine):
Returns a new Observable that multicasts (shares) the original Observable. As long as there is at least one Subscriber this Observable will be subscribed and emitting data. When all subscribers have unsubscribed it will unsubscribe from the source Observable. Because the Observable is multicasting it makes the stream hot. This is an alias for
.publish().refCount()
.
What you might be interested is in creating a BehvaiorSubject
, as it has the notion of "current value". Then your API call will not return anything, it will simply trigger the request.
Here's a proof-of-concept:
export class Service {
public data$ = new BehaviorSubject(null);
public get() {
this.http.get(`some/data`).subscribe(data => this.data$.next(data))
}
}
null
is the value every subscriber will get synchronously when subscribed to data$
. You can use it like this.
this.get()
this.data$.subscribe(data => /* ... */)
Of course, you can wrap this into something nicer, and even start implementing things like cache, etc.
Some useful links:
Upvotes: 2