I have a service that call two method of and webapi. Here's the implementation:
geo.service.ts
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { environment } from 'src/environments/environment';
import { CountryResponse } from '../models/country.interface';
import { StateResponse } from '../models/state.interface';
@Injectable({
providedIn: 'root',
})
export class GeoService {
private apiUrl = environment.apiUrl 'parameter';
constructor(private http: HttpClient) {}
getCountry(): Observable<CountryResponse> {
let requestUrl = this.apiUrl '/country';
return this.http.get<CountryResponse>(requestUrl);
}
getStates(
country_id: string): Observable<StateResponse[]> {
let requestUrl = this.apiUrl '/country/' country_id '/state';
return this.http.get<StateResponse[]>(requestUrl);
}
And my ngOnInit in component
this.geo_service
.getCountry()
.subscribe(
(data) => {
this.country = data; //works fine
},
(err) => {
this.notification_service.showError(
'Se ha producido un error'
);
console.log(err);
}
);
this.geo_service
.getState(
this.country.country_id //undefined
)
.subscribe(
(data) => {
this.state = data;
},
(err) => {
this.notification_service.showError(
'Se ha producido un error'
);
console.log(err);
}
);
the problem is: this.country is undefined on getState call method in ngOnInit and i don't know why, and how to fix it. But the data is ok, because this.country = data works fine as code says.
Angular 12.
Help please!
Calling an API in chain
CodePudding user response:
The problem is simple, you have to synchronize the two async call. When you call the state the country is not ready yet. The simplest think you can do is :
this.geo_service
.getCountry()
.subscribe(
(data) => {
this.country = data
//here call the state one.
this.geo_service.getState(....
}
CodePudding user response:
You mentioned in a comment wanting to avoid nesting calls.
This can be done via RxJs streams.
const getCountryData$ = this.geo_service.getCountry().pipe(
// tap is used for side effects mid stream
tap(data => {
this.country = data;
})
)
Note for the following we're not directly relying on the side effect which in general we want to avoid.
const getStateData$ = getCountryData$.pipe(
switchMap(countryData => {
return this.geo_service.getState(countryData.country_id)
})
).subscribe((data) => { this.state = data; })
Subscribe to getStateData$
triggers the stream .
In the code above I'm mixing imperative (this.country = data
) and reactive coding. Personally I'd not use variables on the component but use the async pipe in the template to pull out the variables and this takes care of unsubscribing.
You can use catchError
to handle error in a similar way to your question.