1

I have 2 observable coming from firebase with angularfire and using rxjs

private boatsCollection: AngularFirestoreCollection<Boat>;
boats: Observable<Boat[]>;

private bookingsCollection: AngularFirestoreCollection<Booking>;
bookings: Observable<Booking[]>;

structures are not marching but bookings collection documents have ID'S containing information about boat (so boat id)

I want to to filter boats and leave only these documents where ids are not matching to bookings collection document(s) ID value.

How this can be achieved with rxjs?

ngOnInit() {
  // Subscribe to route params and get user's selected date
  this.sub = this.route.params.subscribe(params => {
    this.date = Number (params['date']);
    // get bookings where selected date exits
    this.bookingsCollection = this.afs.collection<Booking>('bookings', ref => ref.where('date', '==', this.date));
    this.bookings = this.bookingsCollection.valueChanges({ idField: 'id' });
    // Boats Collection
    this.boatsCollection = this.afs.collection<Boat>('boats');
    this.boats = this.boatsCollection.valueChanges({ idField: 'id' });  
 
    combineLatest(this.boats, this.bookings).subscribe(
      ([boats, bookings]) => {
        const bookingIds = bookings.map(booking => booking.boatID);
        const filteredBoats = boats.filter(boat => !(bookingIds.includes(boat.id)));
        console.log(filteredBoats);
      }, 
    ); 
  });
}
Jason Aller
  • 3,541
  • 28
  • 38
  • 38

1 Answers1

1

There are multiple ways to do it. One way would be get notifications from both the observables simultaneously using RxJS combineLatest function and filter the arrays using Array filter and includes function.

Try the following

ngOnInit() {
  combineLatest(this.boats, this.bookings).subscribe(
    ([boats, bookings]) => {
      const bookingIds = bookings.map(booking => booking.id);
      this.filteredBoats = boats.filter(boat => !(bookingIds.includes(boat.id)));
      console.log(this.filteredBoats);    // <-- correct
    },
    error => { }
  );

  console.log(this.filteredBoats);    // <-- wrong - will print undefined/previous value
}

Now the combineLatest will emit for each emission of any of it's source observables. Also all the source observables should've emitted at least once.

You could use RxJS zip or forkJoin functions based on your requirement. Although remember forkJoin will only emit when all the source observables complete.

Update: Async data

The filteredBoats is assigned asynchronously. Which means by the time console.log is executed outside the subscription, the filteredBoats isn't any values yet. The console.log should be inside the subscription.

More about async data here.

ruth
  • 29,535
  • 4
  • 30
  • 57
  • it returns undefined (this.filteredBoats) while calling ngOnInit as I need it when page loaded – Gocha Gocitashvilli Aug 04 '20 at 08:57
  • Please make sure you're using the correct properties for IDs. I've used `booking.id` and `boad.id` as a placeholder. – ruth Aug 04 '20 at 09:01
  • yes I have edited as needed booking.boatID but I guess I must zip filteredboats before getting values? – Gocha Gocitashvilli Aug 04 '20 at 09:04
  • Where are you printing the values? Please update your code in the question. – ruth Aug 04 '20 at 09:06
  • Yes but where are you seeing that `filteredBoats` variable is undefined? – ruth Aug 04 '20 at 09:09
  • I have added console log just after ); subscribe ends – Gocha Gocitashvilli Aug 04 '20 at 09:11
  • The `filteredBoats` is assigned asynchronously. Which means by the time `console.log` is executed the `filteredBoats` isn't any values yet. The `console.log` should be inside the subscription. I've updated the answer. – ruth Aug 04 '20 at 09:17
  • thank you, before your suggestion I have edited code and also edited my question to get you know complete code. it is showing me results yes, but not filtered, I am getting all my available boats. – Gocha Gocitashvilli Aug 04 '20 at 09:24
  • okay I have solved that also, there was a very small detail related to this this.bookings = this.bookingsCollection.valueChanges({ idField: 'id' }); was missing idfield id parameter. now it works well and I will mark your answer as correct because it is complete for my questioin. thanks – Gocha Gocitashvilli Aug 04 '20 at 09:40