Reputation: 566
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?
Upvotes: 2
Views: 6996
Reputation: 14221
You can use an exhaustMap
to pipe the data to a second http call and then merge the data together when it returns.
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
});
Upvotes: 4
Reputation: 20043
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:
async
pipeThe 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>
@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>
Upvotes: 1