Home > OS >  Merge two call and show results using typeahead ngx-bootstrap?
Merge two call and show results using typeahead ngx-bootstrap?

Time:10-28

I'll show you the working code first:

<input [formControl]="search" 
       [typeahead]="suggestions" 
       typeaheadOptionField="name"   
       (typeaheadOnSelect)="onSelectedDriver($event)">


ngOnInit(){
  public autocompleteValue() {
    this.search.valueChanges.subscribe(
      (selectedValue) => {
        this.searchVal(selectedValue)
      }
    );
  }
}


  public searchVal(val: string) {
    this.suggestions = (new Observable((observer: Observer<string>) => {
      observer.next(val);
    })).pipe(
      switchMap((query: string) => {
        if (query) {
          switch (this.serverApi) {
            case 'Driver':
              return this.getAllDrivers(query); 

            default:
              break;
          }
        }
        return of([]);
      })
    );
  }

and:

  getAllDrivers(query: string) {
    return this.accountsService.getDrivers(query)
      .pipe(
        map((data: any) => {
          data.body?.forEach((driver: IDriver) => {
            driver.name = `${driver.firstName} ${driver.lastName}`
          });
          return data.body || [];
        }),
        tap(() => noop, err => {
          this.errorMessage = err && err.message || 'Something goes wrong';
        })
      )
  }

This works.

But i need to expand functionality.

I need an extension to merge two api calls into one and return the result.

I am to do it but without typeahead with forkJoin..

   this.search.valueChanges.subscribe(val => { 
      let driverLoad = this.service.searchDrivers(val, [{ name: 'name', value: val }]);
      let affiliateLoad = this.service.searchAffiliates(val, [{ name: 'name', value: val }]) 
      forkJoin([driverLoad, affiliateLoad, travelAgenciesLoad]).subscribe(resultsOfSearch => {
        let driverArr = resultsOfSearch[0].body;
        let affiliateArr = resultsOfSearch[1].body; 
        this.autoCompleteResults = [...driverArr as [], ...affiliateArr as []];
        return this.autoCompleteResults
      })
    });

I don't know now to fit this to work.

How do I combine this and return the result?

CodePudding user response:

forkJoin

forkJoin only emits a value when all source observables have completed. Because of this - it may not be suitable for your situation.

forkJoin documentation:

Wait for Observables to complete and then combine last values they emitted; complete immediately if an empty array is passed.

In your code, forkJoin will not emit a value until the observables driverLoad, affiliateLoad, and travelAgenciesLoad have all completed.

combineLatest

If your three Observables are continuous data streams that need to be combined before they complete, a better operator is combineLatest:

combineLatest documentation

Whenever any input Observable emits a value, it computes a formula using the latest values from all the inputs, then emits the output of that formula.

//Define observables before subscribing to valueChanges
let driverLoad = this.service.searchDrivers(val, [{ name: 'name', value: val }]);
let affiliateLoad = this.service.searchAffiliates(val, [{ name: 'name', value: val }]) 
let combinationObservable$: Observable<any>;

// when valueChanges emits a value, find the latest emitted value from driverLoad,
// affiliateLoad, and travelAgenciesLoad
this.search.valueChanges.subscribe(val => {
  combinationObservable$ = combineLatest([driverLoad, affiliateLoad, travelAgenciesLoad]); 
}

//subscribe to combination Observable outside of the valueChanges subscription
combinationObservable$.subscribe(value => {
  console.log(value);
  this.autoCompleteResults = value;
});

Here is a StackBlitz I made to simulate your observables with the combineLatest operator.

CodePudding user response:

You can use rxjs zip (After all observables emit, it will emit values as an array) thus you can get the result in an array directly

import { zip } from 'rxjs';
let driverLoad = this.service.searchDrivers(val, [{ name: 'name', value: val }]).pipe(map(data => data.body));
let affiliateLoad = this.service.searchAffiliates(val, [{ name: 'name', value: val }]).pipe(map(data => data.body)) 

zip(driverLoad , affiliateLoad).subscribe((results: any[]) => console.log(results) )
  • Related