Issue
I have an array of servers object where inside that array I have another observable array of objects, its key is [securityGroups].
ngOnInit(): void {
forkJoin(
this.serverService.getServer(),
this.securityGroupService.getSecurityGroups())
.pipe(takeWhile(() => this.alive))
.subscribe(([servers, groups]) => {
this.servers = servers.map((item) => ({
...item,
securityGroups: this.serverService.getServerById(item.id)
.pipe(map(server => server["security_groups"]))
}))
this.securityGroupArray = groups['security_groups'].map((item) => ({
...item,
expanded: false,
}))
}
How can I map this [securityGroup] key from my server array? since it is an Observable. I would not like to make an asynchronous pipe in html, I would like to save it in a new array
My servers Array payload:
[{id: "1879f47f-1c5e-464b-bb76-e7cc13ef426e", name: "hello", flavor: {…}, securityGroups: Observable}
,
{id: "b9c7e32a-bf99-4250-83cb-13523f9c1604", name: "test01", flavor: {…}, securityGroups: Observable}]
Solution
If I understand correctly, your challenge is that you receive an array of server
s and you need to make a call for each one to retrieve more data (securityGroup
s) and append it to the object.
To accomplish this, you’ll need to "subscribe" to this secondary call somehow, so that you can receive the data. There are several "Higher Order Mapping Operators" that can do this for you, so you don’t have to deal with nested subscriptions. In this case, we can use switchMap
.
In order to make a bunch of calls at one time, we can use forkJoin
to make all the calls at once and receive an array of all the results.
You’re currently using forkJoin
to make 2 different calls, but you aren’t using the results from those calls for the same purpose. I would split them up into separate observables, both for clarity of intent and so they can be used independently:
securityGroups$ = this.securityGroupService.getSecurityGroups().pipe(
map(groups => groups.map(group => ({
...group,
expanded: false
})))
);
servers$ = this.serverService.getServers().pipe(
switchMap(servers => forkJoin(
servers.map(s => this.serverService.getServerById(s.id))
)
.pipe(
map(serversWithGroups => serversWithGroups.map((server, i) => ({
...servers[i],
securityGroups: server.security_groups
}))),
// shareReplay(1) - see comment below
))
);
You could still subscribe in your controller if you want:
ngOnInit() {
this.servers$.subscribe();
this.securityGroups$.subscribe();
}
or your could use the AsyncPipe
in the template:
<ng-container *ngFor="let group of securityGroups$ | async">
<option *ngFor="let server of servers$ | async" [value]="server.id">
<ng-container *ngFor="let secGroup of server.securityGroups">
{{ secGroup.name !== group.name ? server.name : '' }}
</ng-container>
</option>
</ng-container>
If you’re nesting *ngFor
then you can use the shareReplay()
operator to prevent multiple subscriptions from executing your service methods multiple times.