Home > front end >  Angular - Edit a Subject which is a type of Array
Angular - Edit a Subject which is a type of Array

Time:11-30

I'm writing an application where an admin can upgrade the power of a user.

After i send the request to the server to increase a user's power. Im getting back from the server a list of users:

powerUp(id: number) {
return this.http
  .post<Hero[]>(environment.apiUrl   'heroes/powerUp/'   id, {})
  .pipe(
    map((heroes) => {
      this.currentHeroes.next(hero);
    })
  );

}

Here is the subject:

private currentHeroes = new BehaviorSubject<Hero[]>(null);
currentHeroes$ = this.currentHeroes.asObservable();

The thing is that it's not officiant because I can return from the server only the particular user that is getting powered up.

powerUp(id: number) {
    return this.http
      .post<Hero[]>(environment.apiUrl   'heroes/powerUp/'   id, {})
      .pipe(
        map((hero) => {
          // update the particular hero in the currentHeroes subject
        })
      );
  }

My question is how can I modify currentHeroes subject (only the updated user) without.next() without getting back from the server the whole list.

I have tried to following:

  powerUp(id: number) {
    return this.http
      .post<Hero>(environment.apiUrl   'heroes/powerUp/'   id, {})
      .pipe(
        map((updatedHero) => {
          this.currentHeroes$.pipe(
                // doesn't enter here
                map((oldHeroes) => {
                  const index = oldHeroes.findIndex((hero) => hero.id === id);
                  oldHeroes[index] = updatedHero;
                })
              );
            })
          );
      }

the template of the Hero card

export class HeroCardComponent implements OnInit {
  @Input() hero: Hero;

  constructor(public heroService: HeroService, private toastr: ToastrService) {}

  ngOnInit(): void {}

  powerUp(id: number, name: string) {
    this.heroService.powerUp(id).subscribe(() => {
      this.toastr.success(`${name} was successfully powered up!`);
    });
  }

  delete(id: number, name: string) {
    this.heroService.delete(id).subscribe(() => {
      this.toastr.success(`${name} was successfully deleted`);
    });
  }
}

Can someone please explain me how can I manage to do it?

CodePudding user response:

You can just replace the previous currentHeroes$ and it should just work out of the box if you're using the async pipe.

If your template looks like this:

<div *ngFor="let hero of (currentHeroes$ | async)">
  <app-hero-card [hero]="hero"></app-hero-card>
</div>

Your component could be something as follows:

@Component({…})
export class HeroListComponent {

  currentHeroes$: Observable<Hero[]>
  constructor(private heroService: HeroService) {
    this.currentHeroes$ = this.heroService.getHeroes()
  }

  powerUp(heroId: string) {
    this.currentHeroes$ = this.heroService.powerUp(heroId).pipe(
      switchMap(updatedHero => {
          return this.currentHeroes$.pipe(
            map(oldHeroes => {
               const index = oldHeroes.findIndex(hero => hero.id === updatedHero.id)
               oldHeroes[index]=updatedHero
               return oldHeroes
            })
          );
      })
     );
  }
}

And now some explanation of the magic:

  1. HeroService.powerUp(heroId) will return us an observable that will emit the updated hero once the network request finishes
  2. Then we transform that observable by applying the switchMap pipe from RxJS. What switchMap does is that it allows to say; OK whatever happens here it's going to be handled by this other observable (the one we return)
  3. On the returned Observable we're just getting the old currentHeroes$ observable and replacing the old hero by finding it by it's id and updating the array item. And finally returning the updated array

Bonus tip

It'd be way less code if the backend returned the updated list OR you could trigger a refetch

If the backend returns the full list

powerUp(heroId:string) {
  this.currentHeroes$ = this.heroService.powerUp(heroId)
}

If you want to trigger a refetch

powerUp(heroId:string) {
  this.currentHeroes$ = this.heroService.powerUp(heroId).pipe(
    switchMap(_updatedHero => this.heroService.getHeroes())
  );

}
  • Related