Home > database >  How can I return an Observable with data of other Observables using his return value in the others
How can I return an Observable with data of other Observables using his return value in the others

Time:03-29

I have a project in which I want to return an Observable of Hero object. My heroes have multiple id properties to fetch data from other Observables as a Skill or a Rarity. My Observables come from the AngularFire library. Here is my Hero class what I have right now:

export class Hero extends Serializable {
  id?: string;
  name?: string;
  description?: string;
  idRarity?: string;
  rarity?: Rarity;
  stats: Stats;
  idSkill1?: string;
  idSkill2?: string;
  skill1?: Skill;
  skill2?: Skill;
  visual?: string;
  dateAdded?: Date;

  constructor() {
    super();
    this.stats = {};
    this.visual = "assets/images/placeholder.jpg";
  }
  ...

}

  // functions from the HeroService
  getHeroes(): Observable<Hero[]> {
    return this.db.collection<JsonArray>(HeroService.url)
      .valueChanges()
      .pipe(
        map(documents => {
          return documents.map(data=> {
            return this.getHeroFromData(data);
          });
        })
      );
  }

  getHero(id: string): Observable<Hero | undefined> {
    // Returns hero|undefined observable
    return this.getHeroDocument(id).valueChanges()
      .pipe(
        map(data => {
          return data ? this.getHeroFromData(data) : undefined
        })
      );
  }

  getHeroFromData(data:JsonArray) {
    let hero = new Hero().fromJSON(data);
    if (hero.idSkill1 && hero.idSkill2 && hero.idRarity) {
      this.skillService.getSkill(hero.idSkill1).subscribe(skill => hero.skill1 = skill);
      this.skillService.getSkill(hero.idSkill2).subscribe(skill => hero.skill2 = skill);
      this.rarityService.getRarity(hero.idRarity).subscribe(rarity => hero.rarity = rarity);
    }
    return hero;
  }

The issue I'm facing is that when my hero is returned, the data for my properties of rarity and skills are not set yet.

Is there a way to wait for all values to be received from the other Observables before returning the hero object?

CodePudding user response:

RxJs 6.5

forkJoin will full fill your requirement here.

know about mergeMap also

// functions from the HeroService
getHero(id: string): Observable<Hero | undefined> {
  // Returns hero|undefined observable
  return this.getHeroDocument(id)
    .valueChanges()
    .pipe(
      mergeMap((data) => {
        return data ? this.getHeroFromData(data) : of(undefined);
      })
    );
}

getHeroFromData(data: JsonArray): Observable<Hero> {
  let hero = new Hero().fromJSON(data);
  if (hero.idSkill1 && hero.idSkill2 && hero.idRarity) {
    forkJoin({
      skill1: this.skillService.getSkill(hero.idSkill1),
      skill2: this.skillService.getSkil1(hero.idSkill2),
      rarity: this.rarityService.getRarity(hero.idRarity),
    }).pipe(
      map((res) => {
        hero.skill1 = res.skill1;
        hero.skill2 = res.skill2;
        hero.rarity = res.rarity;

        return hero;
      })
    );
  }
}

CodePudding user response:

If your hero and skills are loaded once, you should consider using promises instead of observables. If you cannot change it to the core, you can convert an observable to a promise with .toPromise(). Then you can use Promise.all().

getHeroFromData(data:JsonArray): Promise<Hero> {
    let hero = new Hero().fromJSON(data);
    if (hero.idSkill1 && hero.idSkill2 && hero.idRarity) {
      const promise 1 = this.skillService.getSkill(hero.idSkill1).toPromise().then(skill => hero.skill1 = skill);
      const promise2 = this.skillService.getSkill(hero.idSkill2).subscribe(skill => hero.skill2 = skill);
      const promise3 = this.rarityService.getRarity(hero.idRarity).subscribe(rarity => hero.rarity = rarity);
      return Promise.all([promise1, promise2, promise3]).then(() => hero);
    } else {
      return Promise.resolve(hero);
    }        
  }

Then you must use then when calling this method. Because hero is build in a async way, you must treat the return in a async way. Use then().

  • Related