Reputation: 23257
I need to perform two http requests. One after first one, since values provided by first one are going to be used to make the other http calls.
I also need to update my component when first http is resolved. And also, updated it when second http is also resolved.
interface Vehicle {
readonly id: string;
readonly name: string;
readonly brand: string;
}
First http call is getting name
, and the other one the brand
.
Guess this html component:
<div *ngFor='let vehicle of vehicles$ | async'>
{{vehicle.name}} - {{vehicle.brand}}
</div>
In controller:
vehicles$: Observable<Array<Vehicle>>
ngOnInit() {
this.vehicles$ = this._vehicleService.getVehicles();
// how to go on???
}
where getVehicles
:
getVehicles(): Observable<Array<Vehicle>> {
return http.get("url")
.map(vehicle => <Vehicle>{id: vehicle.id, name: vehicle.name});
}
getVehicleBrand(ids: Array<String>): Observable<Array<VehicleBrand>> {
// build url according ids parameter
return http.get("url")
.map(brand => <VehicleBrand>{id: brand.vehicle_id, brand.name});
}
How could I merge Vehicle
with VehicleBrand
?
Upvotes: 0
Views: 375
Reputation: 23813
Merging data between requests shouldn't be the responsibility of the component. That's what services are for.
Example of what such a service might look (feel free to customize it according to your own needs of course):
const vehiclesIdNameMock: VehicleIdName[] = [
{ id: 'vehicle-1', name: 'Vehicle 1' },
{ id: 'vehicle-2', name: 'Vehicle 2' },
{ id: 'vehicle-3', name: 'Vehicle 3' },
];
const vehiclesIdToBrandMap: { [key: string]: VehicleIdBrand } = {
'vehicle-1': { id: 'vehicle-1', brand: 'Brand A' },
'vehicle-2': { id: 'vehicle-2', brand: 'Brand A' },
'vehicle-3': { id: 'vehicle-3', brand: 'Brand B' },
}
@Injectable()
export class VehiclesService {
private getVehiclesIdName(): Observable<VehicleIdName[]> {
return of(vehiclesIdNameMock).pipe(delay(500));
}
private getVehicleIdBrand(id: string): Observable<VehicleIdBrand> {
return of(vehiclesIdToBrandMap[id]).pipe(delay(500));
}
public getVehicles(): Observable<Vehicle[]>{
return this.getVehiclesIdName().pipe(
mergeMap(vehiclesIdName => {
const requests: Observable<VehicleIdBrand>[] = vehiclesIdName.map(vehicleIdName => this.getVehicleIdBrand(vehicleIdName.id))
return forkJoin(requests).pipe(
map(vehiclesIdBrand => vehiclesIdBrand.map(
(vehiclesIdBrand, index) => ({ ...vehiclesIdBrand, ...vehiclesIdName[index] })
))
);
})
)
}
}
The 2 private methods here are mocked because I obviously don't have access to your endpoint but as long as your return the same data type that'll be fine.
Then your VehicleComponent would be as clean as:
@Component({
selector: 'app-vehicles',
templateUrl: './vehicles.component.html',
styleUrls: ['./vehicles.component.css']
})
export class VehiclesComponent {
public vehicles$: Observable<Vehicle[]> = this.vehiclesService.getVehicles();
constructor(private vehiclesService: VehiclesService) {}
}
Here's a live demo on Stackblitz https://stackblitz.com/edit/angular-et3oug
Upvotes: 1
Reputation: 42556
If you want to return observables in a sequential manner, you can make use of RxJS's mergeMap to map over the observable values from the service's getVehicles()
into an inner observable, to get the list of ids. Then, we call the getVehicleBrand()
method from the service, and the observables are returned in subscribe() on the subsequent line.
From there, you can match the brand name with the respective vehicles.
On your component.ts, this is how it will look like
ngOnInit() {
this._vehicleService.getVehicles(["1", "2"]).pipe(
mergeMap(vehicles => {
const vehicleIdList: string[] = vehicles.map(vehicle => vehicle.id);
return this._vehicleService.getVehicleBrand(vehicleIdList);
})
).subscribe(res => {
// continue the rest
})
}
Upvotes: 0
Reputation: 2317
You can use async
await
here is the example.
Controller
vehicles$: Observable<Array<Vehicle>>
ngOnInit() {
this.getVehicle();
}
async getVehicle() {
this.vehicles$ = this._vehicleService.getVehicles(["1", "2"]);
await getVehicleBrand([vehicalIds]);
// other code
}
getVehicleBrand(ids: Array<String>): Observable<Array<VehicleBrand>> {
return new Promise(resolve => {
// build url according ids parameter
http.get("url")
.map(brand => <VehicleBrand>{id: brand.vehicle_id, brand.name});
resolve();
})
}
Upvotes: 0
Reputation: 10555
You could do something like this:
http.get("url").pipe(
map(vehicle => <Vehicle>{id: vehicle.id, name: vehicle.name})
tap(vehicle => { this.vehicles$.next(vehicle); })
switchMap(vehicle => this.getVehicleBrand([vehicle.id]));
)
Upvotes: 0