Reputation: 13997
What is the best way in angular with rxjs to already display the result of a first observable, and combining the data when other observables are finished?
Example:
@Component({
selector: 'app-component',
template: `
<div *ngFor="let group of groups$ | async">
<div *ngFor="let thisItem of group.theseItems">
...
</div>
<div *ngFor="let thatItem of group.thoseItems">
...
</div>
</div>
`
})
export class AppComponent implements OnInit {
...
ngOnInit() {
this.groups$ = this.http.get<IThisItem[]>('api/theseItems').pipe(
map(theseItems => {
return theseItems.groupBy('groupCode');
})
);
// combine these results? This operation can take 5 seconds
this.groups$$ = this.http.get<IThatItem[]>('api/thoseItems').pipe(
map(thoseItems => {
return thoseItems.groupBy('groupCode');
})
);
}
}
I understand it can be done by subscribing to both, and merge the results. But is it possible to use pipe operators for this, and using the async
pipe?
Upvotes: 3
Views: 2311
Reputation: 26450
I think you could go with the combineLatest
rxjs operator. This might mean that you change the handling in your template a bit too.
I could not use your example as I don't know your get functions but basically the same principle applies.
Check stackblitz here for an example:
export class AppComponent {
private firstObservable = of([{name: 'name1'}]).pipe(startWith([]));
private secondObservable = of([{name: 'name2'}]).pipe(startWith([]));
combined = combineLatest(this.firstObservable, this.secondObservable).pipe(
map(([firstResult, secondResult]) => {
return [].concat(firstResult).concat(secondResult)
})
);
}
Html output:
<span *ngFor="let item of combined | async">{{item.name}}<span>
Upvotes: 4
Reputation: 2699
You could use merge and scan.
first$: Observable<Post[]> = this.http.get<Post[]>('https://jsonplaceholder.typicode.com/posts?userId=1');
second$: Observable<Post[]> = this.http.get<Post[]>('https://jsonplaceholder.typicode.com/posts?userId=2');
combinedPosts$: Observable<Post[]> = merge(this.first$, this.second$).pipe(
scan((acc: Post[], curr: Post[]) => [...acc, ...curr], [])
)
https://www.learnrxjs.io/operators/combination/merge.html Make one observable from many.
https://www.learnrxjs.io/operators/transformation/scan.html Scan is similar to array.reduce... you can accumulate the results of each observable emission.
Working example: https://stackblitz.com/edit/angular-lrwwxw
The combineLatest
operator is less ideal, since it requires that each observable emits before the combined observable can emit: https://www.learnrxjs.io/operators/combination/combinelatest.html
Be aware that combineLatest will not emit an initial value until each observable emits at least one value.
Upvotes: 2
Reputation: 3593
Async pipe is just a subscriber to an observable... To answer to your question you can use any possible ways... For example:
<div *ngFor="let group of groups$ | async as groups">
<div *ngFor="let thisItem of group.theseItems">
...
</div>
</div>
public groups$: Observable<type> = this.http.get<IThatItem[]>.pipe(
startWith(INITIAL_VALUE)
);
or
public groups$: Observable<type> = combineLatest(
of(INITIAL_VALUE),
this.http.get<IThatItem[]>
)
Upvotes: 1