Angular2 Observable share is not working and duplicate http calls going
BuildingService.ts
@Injectable()
export class BuildingService {
constructor(private http: Http){
}
buildings$: Observable<Building[]>;
this.buildings: Building[];
getData() : Observable<Building[]>{
this.buildings$ = this.http.get('http://localhost:8080/buildings').share().map(this.extractData);
this.buildings$.subscribe(buildings => this.buildings = buildings);
return this.buildings$;
}
private extractData(res: Response) {
let body = res.json();
return body;
}
}
component1.ts
export class component1 {
constructor( private buildingService: BuildingService) {}
this.subscription = this.buildingService.getData()
.subscribe(buildings => console.log(buildings),
error => this.errorMessage = <any>error);
}
component2.ts
export class component2 {
constructor( private buildingService: BuildingService) {}
this.subscription = this.buildingService.getData()
.subscribe(buildings => console.log(buildings),
error => this.errorMessage = <any>error);
}
share is not working, multiple http calls are going. Even I tried code from this link
but no use.
Can somebody please let me know how to avoid duplicate http calls with Angular Observable?
I think this is just misunderstanding of what share()
does.
When you call this.buildings$.subscribe(...)
it makes a ConnectableObservable
thanks to share()
operator which is immediately followed by connect()
.
If you make another subscription while the HTTP request is pending it will just add another Observer to the ConnectableObservable
and when the response is ready it'll be sent to both Observers. However if you let this.buildings$
to complete and after that you subscribe again it'll make another HTTP request because the ConnectableObservable
is not connected to its source.
What you want instead is .publishReplay(1).refCount()
(or shareReplay(1)
since RxJS 5.4.0) that replays the last item emitted from the source. Very likely you'll also want to append take(1)
to properly complete the chain.
You are creating a new stream with every .getData()
-call here:
this.buildings$ = this.http.get('http://localhost:8080/buildings').share().map(this.extractData);
If you want to "share" data between components and prevent multiple rest-calls, you would have to most likely use the replay-feature of rxjs, you could for example do something like this:
@Injectable()
export class BuildingService {
constructor(private http: Http){}
buildings$: Observable<Building[]>;
this.buildings: Building[];
getData(fetchNew: boolean = false) : Observable<Building[]>{
if (fetchNew || !this.buildings$) {
this.buildings$ = this.http.get('http://localhost:8080/buildings')
.map(this.extractData)
.publishReplay(1)
.refCount();
this.buildings$.subscribe(buildings => this.buildings = buildings);
}
return this.buildings$;
}
private extractData(res: Response) {
let body = res.json();
return body;
}
}
publishReplay(1)
will re-emit("replay") the last emitted data to future subscribers, so no new call will be made.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With