I'm sending a request to the server to modify one of the users.
As a response, the server sends back the updated user.
After that I'm trying to update the BehaviorSubject of all the users like this:
private currentHeroes = new BehaviorSubject<Hero[]>(null);
currentHeroes$ = this.currentHeroes.asObservable();
powerUp(id: number) {
return this.http
.post<Hero>(environment.apiUrl 'heroes/powerUp/' id, {})
.pipe(
map((updatedHero) => {
this.currentHeroes$.pipe(
// The code doesn't reach here
map((oldHeroes) => {
const index = oldHeroes.findIndex((hero) => hero.id === id);
oldHeroes[index] = updatedHero;
})
);
})
);
}
In case of deleting:
delete(id: number) {
return this.http.delete<Hero>(environment.apiUrl 'heroes/' id).pipe(
tap((deletedHero: Hero) => {
this.currentHeroes.value.filter(
(hero: Hero) => hero.id !== deletedHero.id
);
})
);
}
I'm trying to modify the array of users. Replace the old user with the updated user.
And the code doesn't reach the pipe of the currentHeroes$. I'm not really sure why is that happening or how can I fix it.
I have tried to return a value from the pipe or use tap instead of map but no luck.
Does any can maybe take a look to explain to me why does it happen? or how can I fix it?
Thanks!
CodePudding user response:
Updating an object in array
Variant 1: Using value
getter of BehaviorSubject
You don't have to pipe
to the BehaviorSubject
. You'd just need to use the tap
operator on the source observable (HTTP request) and update the BehaviorSubject
in it's callback. tap
operator is akin to map
but wouldn't transform the incoming emissions. So it's ideal for performing side-effects like updating a local variable.
import { tap } from 'rxjs/operators';
powerUp(id: number) {
return this.http.post<Hero>(environment.apiUrl 'heroes/powerUp/' id, {}).pipe(
tap((updatedHero: any) => {
this.currentHeroes.next(
this.currenHeroes.value.map((hero: Hero) => // <-- `Array#map` function
hero.id === updatedHero.id ? updatedHero : hero
)
);
}
})
);
}
Variant 2: Using withLatestFrom
operator
If you do not wish to use the value
getter from the BehaviorSubject
, you could try to use the withLatestFrom
operator to fetch it's latest value.
import { tap, withLatestFrom } from 'rxjs/operators';
powerUp(id: number) {
return this.http.post<Hero>(environment.apiUrl 'heroes/powerUp/' id, {}).pipe(
withLatestFrom(this.currentHeroes$),
tap(([updatedHero, currentHeroes]) => {
this.currentHeroes.next(
currenHeroes.map((hero: Hero) =>
hero.id === updatedHero.id ? updatedHero : hero
)
);
}
})
);
}
Deleting an object in array
As mentioned in the comments, to delete an element in the array, you could use the Array#filter
instead of Array#map
function.
Variant 1: Using value
getter of BehaviorSubject
import { tap, withLatestFrom } from 'rxjs/operators';
powerUp(id: number) {
return this.http.delete<Hero>(environment.apiUrl 'heroes/' id).pipe(
tap(([deletedHero, currentHeroes]) => {
this.currentHeroes.next(
this.currentHeroes.value.filter((hero: Hero) => hero.id !== deletedHero.id)
);
}
})
);
}
Variant 2: Using withLatestFrom
operator
import { tap, withLatestFrom } from 'rxjs/operators';
powerUp(id: number) {
return this.http.delete<Hero>(environment.apiUrl 'heroes/' id).pipe(
withLatestFrom(this.currentHeroes$),
tap(([deletedHero, currentHeroes]) => {
this.currentHeroes.next(
currentHeroes.filter((hero: Hero) => hero.id !== deletedHero.id)
);
}
})
);
}