Home > Net >  Using forkJoin to run multiple concurrent queries with AngularFire
Using forkJoin to run multiple concurrent queries with AngularFire

Time:10-16

The Problem

I am using AngularFire2 and want to return the data associated with two or more contactNames.

For example querying my Realtime database for contactNames Steve and Brandon:

permits: {
  1: {
    permit: '12345',
    contactName: 'Steve'
  },
  2: {
    permit: '45678',
    contactName: 'Brandon'
  },
  3: {
    permit: '78910',
    contactName: 'Kevin'
  },
  4: {
    permit: '54321',
    contactName: 'Steve'
  },
}

Would return

  1: {
    permit: '12345',
    contactName: 'Steve'
  },
  2: {
    permit: '45678',
    contactName: 'Brandon'
  },
  4: {
    permit: '54321',
    contactName: 'Steve'
  },

My Solution Attempt

I am using map to loop through and store observables in an array

contactNamesFilter: string[] = ['Steve', 'Brandon'];

const requests = this.contactNamesFilter
  .map(contactName => this.permitBrowserService.getData(contactName));

and forkJoin to join them all into one observable. (I am using RxJS v6.6.3)

forkJoin(requests).subscribe(console.log);

The problem is that subscribing to the forkJoin observable does not return anything. It appears the code is dead and is not running. I know forkJoin won't return anything until all observables have returned something. I suspect it has something to do with the permitBrowserService.getData() observable being technically the same.

What am I doing wrong? Is there a better approach to this problem?

Other Troubleshooting

I tried explicitly writing out the observable sources with the same result:

forkJoin({
  sourceOne: this.permitBrowserService.getData('Steve'),
  sourceTwo: this.permitBrowserService.getData('Brandon'),
}).subscribe(console.log);

Backup

I am using AngularFire2 to query the permit list:

export class PermitBrowserService {
  permitData$: Observable<AngularFireAction<DataSnapshot>[]>;
  contactName$: BehaviorSubject<string|null>;

  constructor(
    public db: AngularFireDatabase,
  ) {
    this.contactName$ = new BehaviorSubject(null);
    this.permitData$ = this.contactName$.pipe(
      switchMap(contactName => 
        db.list('/permits', ref =>
          contactName ? ref.orderByChild('contactName').equalTo(contactName) : ref
        ).snapshotChanges()
      )
    );
  }

  getData(contactNameFilter?: string | null): Observable<WellPermit[]> {
    if (contactNameFilter) {
      this.contactName$.next(contactNameFilter);
    }
    return this.permitData$.pipe(
      map(changes => {
        return changes.map(c => {
          const data = c.payload.val();
          const id = c.key;
          return { id, ...data };
        })
      })
    );
  }

CodePudding user response:

Since your this.permitData$ (permitBrowserService.getData()) is never going to be complete, forkJoin will never be going to emit anything. So use combineLatest, it will emit upon each this.permitData$ emit.

combineLatest({
  sourceOne: this.permitBrowserService.getData('Steve'),
  sourceTwo: this.permitBrowserService.getData('Brandon'),
}).subscribe(console.log);

Or

const requests = this.contactNamesFilter
  .map(contactName => this.permitBrowserService.getData(contactName));

combineLatest([...requests]).subscribe(console.log);

combineLatest

Combines multiple Observables to create an Observable whose values are calculated from the latest values of each of its input Observables.

forkJoin

Accepts an Array of ObservableInput or a dictionary Object of ObservableInput and returns an Observable that emits either an array of values in the exact same order as the passed array, or a dictionary of values in the same shape as the passed dictionary.

  • Related