0

I'm building an Angular app for a Travel Agency. In a hotel listing page, I need to show Country and City of the hotel. I'm getting Country, City and Hotel data from ngrx/data EntityService.

If I use nested subscription it work fine, but I'm sure there's a better of way of doing this.

Here's my current implementation

this.countryService.entities$.pipe(map((countries: Country[]) => countries)).subscribe((countries) => {
      this.countries = countries;
      this.cityService.entities$.pipe(map((cities) => cities)).subscribe((cities) => {
        this.cities = cities;
        this.hotelService.entities$.pipe(map((hotels) => hotels)).subscribe((hotels) => {
          this.hotels = hotels.map((hotel) => {
            return {
              ...hotel,
              country: this.countries.find((c) => c.id === hotel.countryId),
              city: this.cities.find((c) => c.id === hotel.cityId),
            };
          });
        });
      });
    });

Could anybody sugget a better alternative for the above solution

Nesar
  • 5,599
  • 2
  • 22
  • 21

2 Answers2

1

you can use the zip operator to combine the observables. There are a few others as well liek combineLatest, merge etc. Have a read of the official documents and decide which one you want to use for yourself.

 zip(this.countryService.entities$, this.cityService.entities$, this.hotelService.entities$).pipe(map(response => {
       return {
         countries: response[0],
         cities: response[1],
         hotels: response[2],
       };
    }).subscribe((respObj: {countries: Countries[], cities: Cities[], hotels: Hotels[]}) => {
       this.countries = respObj.countries;
       this.cities = respObj.cities;
       this.hotels = respObj.this.hotels;
    }));

PS: this is untested code. just have refactored.

arbghl
  • 358
  • 2
  • 14
  • This solves 1 part of the problem, still need to assign country and city, after getting the hotels – Nesar Oct 03 '20 at 06:06
  • yes, it was just to show various ways to combine the multiple observables. You can add your business logic as you need. Hope that helped. – arbghl Oct 03 '20 at 06:39
  • it did, i'm new to rxjs. Seems the implementation of zip and CombileLatest are same, is there any difference, like performance wise, or execution order? – Nesar Oct 03 '20 at 06:45
  • 1
    Yes its quite similar, except for the when how zip and combineLatest emits values. this article explains it pretty well. https://medium.com/wavy-engineering/rxjs-observables-zip-vs-combinelatest-95098ac4b2b8 In most cases when yo do not have to worry about order of the observale values, I feel zip is a bit more efficient in terms of memory as it emits only when all have emitted. – arbghl Oct 03 '20 at 10:12
1

I would use the rxjs combineLatest operator for subscribing to multiple observable. Following is the illustration of your code using combineLatest operator.

combineLatest([
    this.countryService.entities$,
    this.cityService.entities$,
    this.hotelService.entities$
]).subscribe(([countries, cities, hotels]) => {
    this.countries = countries;
    this.cities = cities;
    this.hotels = hotels.map((hotel) => {
        return {
            ...hotel,
            country: this.countries.find((c) => c.id === hotel.countryId),
            city: this.cities.find((c) => c.id === hotel.cityId)
        }
    });
});
Neo
  • 26
  • 4
  • You missed the return statement. After adding return it worked! Thanks combineLatest([this.countryService.entities$, this.cityService.entities$, this.hotelService.entities$]).subscribe( ([countries, cities, hotels]) => { this.countries = countries; this.cities = cities; this.hotels = hotels.map((hotel) => { return { ...hotel, country: this.countries.find((c) => c.id === hotel.countryId), city: this.cities.find((c) => c.id === hotel.cityId), }; }); } ); – Nesar Oct 03 '20 at 06:04
  • Yeah it seems i missed the return. Thanks for correcting me. – Neo Oct 03 '20 at 06:06
  • You are welcome, i've added return in you answer. thanks again – Nesar Oct 03 '20 at 06:08
  • You should also unsubscribe from the observable during component destroy. If you don't unsubscribe there will me a memory leak. – Neo Oct 03 '20 at 06:20
  • Right, yep, i'm aware of that. thanks for reminding though :) – Nesar Oct 03 '20 at 06:21
  • Also combineLatest can not influence the order in which the values of the observables are emitted. So beware of it before using it everywhere. – Neo Oct 03 '20 at 06:23
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/222434/discussion-between-nesar-and-neo). – Nesar Oct 03 '20 at 06:27