Reputation: 394
I have a situation where I need to run a loop and fetch respective items' description. Then along with the item Ids and other information I have to include the fetched description in a datatable.
addDescription(){
this.arrayOfItems.forEach(element => {
// CALL a function which will make a service call
this.fetchDescription(element);
//CODE TO DECLARE AN INTERFACE TO ASSIGN RESPECTIVE VALUES. eg.
// ITEM_ID : element.id,
// ITEM_DEF : this.fetchedDescription.join("\n")
}
Function body:
fetchDescription(elementToFetchDesc){
//Declaring HTTPPARAMS in PassingParams variable
this.componentService.getDesc(PassingParams)
.subscribe((response: HttpResponse<any>) => {
if(response.status ==200){
this.fetchedDescription = reponse.body.output.slice(6,-1);
}
//Code if response status is NOT 200
}
In componentService
Service:
construcutor(private httpConn: HttpClient){}
getDesc(Params){
// Declare URL
return this.httpConn.get(URL, {params: Params, observe: 'response'});
}
The problem:
As it's running in a loop and the subscription is an async call, so, after running the loop in forEach
it's coming out. As a result the description is not getting assigned to the variable (ITEM_DEF
) in the interface.
To solve this issue I implemented a little change to use promise
.
In service I added:
import 'rxjs/add/operator/toPromise';
And updated the service method as:
return this.httpConn.get(URL, {params: Params, observe: 'response'})
.toPromise(); // converted observable into promise
Also changed in component:
Inside fetchDescription
function:
replaced .subscribe
as .then
But the issue still persists. Please let me know where I'm doing wrong to implement this logic.
Upvotes: 2
Views: 8933
Reputation: 659
For this you should use rxjs.
checkStatus(url: string, id: string): Observable<string> {
const trigger$ = new Subject<string>();
const time = Date.now();
return trigger$.asObservable().startWith(url).concatMap((u) => {
return this.ping(u).map(() => {
trigger$.complete();
return id;
}).catch((err) => {
trigger$.next(u);
return Observable.empty<any>();
});
});
}
protected ping(url: string): Observable<any> {
return this.http.get(url)
.catch(err => Observable.throw(err));
}
Here the concatMap operator will fire the next observable only if the first one is complete i.e the response of first API call is available. Whenever you call the method complete()
on the trigger$ subject it will complete the observable and the API calls. You might have to change the logic of calling complete()
.
Upvotes: 0
Reputation: 379
Solution:
Function Body:
fetchDescription(elementToFetchDesc): Observable<string> {
//Declaring HTTPPARAMS in PassingParams variable
return this.componentService.getDesc(PassingParams)
.map((response: HttpResponse<any>) => {
if(response.status ==200){
return reponse.body.output.slice(6,-1);
}
});
}
}
Call:
this.fetchDescription(element).subscribe((description: string) => {
ITEM_ID : element.id,
ITEM_DEF : description
});
Upvotes: 1
Reputation: 1596
the solution is to convert observable to promise but not using then!
Example:
This is your service function that sends the request:
myRequest(num) {
return this.http.get('http://someUrl.com/' + num).toPromise();
}
This is the function to send all requests in a component class:
async sendAll() {
let response = [];
for(let i = 0; i < 5; i++) {
response[i] = await this.myService.myRequest();
}
// Got all the results!
}
Upvotes: 2