Home > front end >  Angular - edit a BehaviorSubject Array
Angular - edit a BehaviorSubject Array

Time:11-30

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)
        );
      }
    })
  );
}
  • Related