2

I am currently writing an ngbTypeahead search and I am stuck because I have never really worked with Observables, which are the expected return type of the ngbTypeahead search.

The search function in my component looks like this:

  search: OperatorFunction<string,  readonly LoqateResponseModel[]> = (text$: Observable<string>) => {
return text$.pipe(
  switchMap(term => this.addressService.searchAddress(term, this.countryCode)),
  map(results => {
    const searchResults = results[LoqateKeyEnum.ITEMS] as [];
    const searchResultLoqateModels: LoqateResponseModel[] = [];
    searchResults.forEach(result => {
      searchResultLoqateModels.push(new LoqateResponseModel(
        result[LoqateKeyEnum.ID],
        result[LoqateKeyEnum.TYPE],
        result[LoqateKeyEnum.TEXT],
        result[LoqateKeyEnum.HIGHLIGHT],
        result[LoqateKeyEnum.DESCRIPTION]));
    });
    return searchResultLoqateModels;
  })
);

}

resultFormatter = (loqateResponse: LoqateResponseModel): string => loqateResponse.display();

I am conducting a loqate search and am storing the results as model objects in a list and return them.

public searchAddress(searchValue, countryCode): Observable<object> 
  {
    const httpParams = new HttpParams();
    return this.httpClient.post(this.addressSearchUrl, {}, {
      headers: this.headers,
      params: new HttpParams()
        .set('Key', loqateKey)
        .set('Text', searchValue)
        .set('Origin', countryCode)
    });
  }

The Model looks like this:

export class LoqateResponseModel {
  constructor(
    public id: string,
    public type: LoqateTypeEnum,
    public text: string,
    public highlight: string,
    public description: string) {
  }

  public isAddress(): boolean { return this.type === LoqateTypeEnum.ADDRESS; }
  public display(): string { return this.text + ', ' + this.description; }
}

Now I thought, that a list of LoqateResponseModels is stored as result of the search and then each of these list items are being formatted properly to display in the typeahead popup through the resultFormatter.

tldr: I want to search something with the ngbTypeahead and query the search term from an API endpoint and display the search results in the typeahead popup.

Edit: I've edited the answer, this code is working.

BoJack Horseman
  • 4,406
  • 13
  • 38
  • 70
  • 1
    Can you please specify what is working and what is not working. I don't understand exactly what the issue is. – BizzyBob Mar 30 '21 at 14:01
  • I've edited my code to remove misleading code. I have an ngbTypeahead where a user should be able to search an address. He types in a few characters and the search function is called. In the search function I want to call an API that returns search results. I need to subscribe to those search results and still return an OperatorFunction because that is ngbTypeaheads requirement. I really can't figure out how to do that. – BoJack Horseman Mar 30 '21 at 15:24

1 Answers1

2

I think you are looking for switchMap. This operator will subscribe you an observable source and emit its results:

You don't want to return null, you simply return the observable with your piped modification.

Your operator function should take an observable and return an observable. Your observable can simply use switchMap to map the incoming text to the api call.

search: OperatorFunction<string, readonly LoqateResponseModel[]> = text$ => 
    text$.pipe(
        switchMap(text => this.searchAddress(text))
    );

Each time switchMap receives some text, it will do a few things for you:

  1. subscribe to searchAddress(text)
  2. emit results from this subscription
  3. stop emitting results from previous subscriptions
BizzyBob
  • 12,309
  • 4
  • 27
  • 51
  • Thank you for your answer. I've edited my question with the code you provided, but it doesn't show any search suggestions. I want to transform the json response items into loqate model instances and save those in my Observable so that I can use them for later. And in order to display a readable string I use the resultFormatter. It just doesn't work as expected though... The reason why I need to store the models and not strings in the returned observable is because I need to access the loqate models ID later on when the user selects a search suggestion – BoJack Horseman Mar 30 '21 at 15:55
  • 1
    Are there any errors in the console? Can you tell if the call to `addressService.searchAddress()` actually being executed? – BizzyBob Mar 30 '21 at 15:58
  • There are no errors. You are right, it is not being executed. But why though – BoJack Horseman Mar 30 '21 at 16:03
  • It doen't look like your are acutally using the resultFormatter. You can further modify your stream by applying the `resultFormatter`, which might be all you need to do. I'm not familar with the ngb stuff or the Loqate stuff. – BizzyBob Mar 30 '21 at 16:03
  • I now know why, it says: `Type (text$: Observable) => void is not assignable to type (text: Observable) => Observable` – BoJack Horseman Mar 30 '21 at 16:04
  • 1
    oh, there you go! looks like you need to add a `return` or remove the surrounding `{}` – BizzyBob Mar 30 '21 at 16:06
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/230553/discussion-between-bojack-horseman-and-bizzybob). – BoJack Horseman Mar 30 '21 at 16:14