I make a request to the server and get the response as observable. Inside the data, I have an ID which refers to another document, which I need to send another request to fetch the data and append it with the data of the parent Observable. How to achieve this?
With Promise, I can use async/await to do this, but I want to know how to achieve this using Observables
Sample with promise
async func_name() {
let data1 = await <first_server_call_returns_promise>();
let data2 = await <second_call_with_data1.id_returns_promise>();
data1.value = data2;
return data1;
}
I make a request to the server and get the response as observable. Inside the data, I have an ID which refers to another document, which I need to send another request to fetch the data and append it with the data of the parent Observable. How to achieve this?
With Promise, I can use async/await to do this, but I want to know how to achieve this using Observables
Sample with promise
async func_name() {
let data1 = await <first_server_call_returns_promise>();
let data2 = await <second_call_with_data1.id_returns_promise>();
data1.value = data2;
return data1;
}
How to achieve the above with Observables?
Edit 1
Based on @TeddySterne reply the code I wrote is below but I'm getting an observable within the subscribe instead of actual data which prevent me to assign the observable to html
this.blogDoc.snapshotChanges().pipe(
exhaustMap((blogs) => {
return blogs.map(blog => {
const data = blog.payload.doc.data();
const id = blog.payload.doc.id;
return this.userService.getUserById(data.uid).pipe(
map((user) => {
const name = user.name;
return { id, name, ...data}
}),
)
})
}),
)
.subscribe(finalData => {
finalData.subscribe(data => console.log(data));
});
Edit 2
Below is the updated code that I have now
this.blogDoc.snapshotChanges().pipe(
exhaustMap((blogs: any[]) => {
return zip(blogs.map((blog) => {
const data = blog.payload.doc.data();
const id = blog.payload.doc.id;
console.log(data);
return this.userService.getUserById(data.uid).pipe(
map((value) => {
console.log(value);
return value;
})
)
})).pipe(first())
})
).subscribe(values => console.log("Values: ", values));
The
console.log(data)
prints the data correctly but console.log(value)
is not getting printed nor the console.log("Values: ", values)
When I use subscribe on this.userService.getUserById(data.uid)
, it returns the expected single user document.
What am I missing here?
Share Improve this question edited May 11, 2018 at 21:30 Sesha asked May 10, 2018 at 19:34 SeshaSesha 5661 gold badge7 silver badges22 bronze badges 2-
You can't, unless you convert them
.toPromise()
. – jonrsharpe Commented May 10, 2018 at 19:39 - Its not necessary that I should use async/await. If there's any other possible way? – Sesha Commented May 10, 2018 at 19:41
2 Answers
Reset to default 4You can use an exhaustMap
to pipe the data to a second http call and then merge the data together when it returns.
Example
import { zip } from 'rxjs/observable/zip';
this.http.get('/data1').pipe(
exhaustMap((data1Arr: any[]) => {
return zip(...data1Arr.map((data1) => {
return this.http.get('/data2/' + data1.id).pipe(
map((data2) => {
data1.value = data2;
return data1;
}),
);
}).pipe(first());
}),
).subscribe((fullQualifiedData) => {
// do stuff with the data
});
Example
I tried the above code, but what I'm getting in fullQualifiedData is another observable, which I can't use directly on *ngFor in html file.
This is discussed in the Angular docs as well, but essentially you have two solutions:
Using the async
pipe
The async
pipe will subscribe to the observable for you and then provide the data. This is easy to use, but note that every async
pipe will create a new subscription, which in the case of HTTP requests can mean firing that request multiple times. So use with care.
@Component({ … })
export class YourComponent {
public data$ = this.http.getFoo();
constructor(private http: Http) {}
}
<ng-container *ngIf="data$ | async as data">
{{ data.foobar }}
</ng-container>
Manually managing the subscription
@Component({ … })
export class YourComponent implements OnInit {
public data: Data;
constructor(private http: Http) {}
public ngOnInit() {
this.http.getFoo().subscribe(data => this.data = data);
}
}
{{ data?.foobar }}
or
<ng-container *ngIf="data">
{{ data.foobar }}
</ng-container>