简体   繁体   中英

How to use ngx-bootstrap typeahead with async httpclient call

I'm attempting to populate nxg-bootstrap typeahead with async results from a rest backend in Angular 4. Their site has an example ( https://valor-software.com/ngx-bootstrap/#/typeahead ) on how to do this with mock observable data, but I am struggling doing this with httpclient. All examples of people using this are using the old Http module instead of the new HttpClient module used in Angular 4.

This is their example:

import { Component } from '@angular/core';

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import { TypeaheadMatch } from 'ngx-bootstrap/typeahead';

@Component({
  selector: 'demo-typeahead-async',
  templateUrl: './async.html'
})
export class DemoTypeaheadAsyncComponent {
  asyncSelected: string;
  typeaheadLoading: boolean;
  typeaheadNoResults: boolean;
  dataSource: Observable<any>;
  statesComplex: any[] = [
    { id: 1, name: 'Alabama', region: 'South' },
    { id: 2, name: 'Alaska', region: 'West' },
    {
      id: 3,
      name: 'Arizona',
      region: 'West'
    },
    { id: 4, name: 'Arkansas', region: 'South' },
    { id: 5, name: 'California', region: 'West' },
    { id: 6, name: 'Colorado', region: 'West' },
    { id: 7, name: 'Connecticut', region: 'Northeast' },
    { id: 8, name: 'Delaware', region: 'South' },
    { id: 9, name: 'Florida', region: 'South' },
    { id: 10, name: 'Georgia', region: 'South' },
    { id: 11, name: 'Hawaii', region: 'West' },
    { id: 12, name: 'Idaho', region: 'West' },
    { id: 13, name: 'Illinois', region: 'Midwest' },
    { id: 14, name: 'Indiana', region: 'Midwest' },
    { id: 15, name: 'Iowa', region: 'Midwest' },
    { id: 16, name: 'Kansas', region: 'Midwest' },
    { id: 17, name: 'Kentucky', region: 'South' },
    { id: 18, name: 'Louisiana', region: 'South' },
    { id: 19, name: 'Maine', region: 'Northeast' },
    { id: 21, name: 'Maryland', region: 'South' },
    { id: 22, name: 'Massachusetts', region: 'Northeast' },
    { id: 23, name: 'Michigan', region: 'Midwest' },
    { id: 24, name: 'Minnesota', region: 'Midwest' },
    { id: 25, name: 'Mississippi', region: 'South' },
    { id: 26, name: 'Missouri', region: 'Midwest' },
    { id: 27, name: 'Montana', region: 'West' },
    { id: 28, name: 'Nebraska', region: 'Midwest' },
    { id: 29, name: 'Nevada', region: 'West' },
    { id: 30, name: 'New Hampshire', region: 'Northeast' },
    { id: 31, name: 'New Jersey', region: 'Northeast' },
    { id: 32, name: 'New Mexico', region: 'West' },
    { id: 33, name: 'New York', region: 'Northeast' },
    { id: 34, name: 'North Dakota', region: 'Midwest' },
    { id: 35, name: 'North Carolina', region: 'South' },
    { id: 36, name: 'Ohio', region: 'Midwest' },
    { id: 37, name: 'Oklahoma', region: 'South' },
    { id: 38, name: 'Oregon', region: 'West' },
    { id: 39, name: 'Pennsylvania', region: 'Northeast' },
    { id: 40, name: 'Rhode Island', region: 'Northeast' },
    { id: 41, name: 'South Carolina', region: 'South' },
    { id: 42, name: 'South Dakota', region: 'Midwest' },
    { id: 43, name: 'Tennessee', region: 'South' },
    { id: 44, name: 'Texas', region: 'South' },
    { id: 45, name: 'Utah', region: 'West' },
    { id: 46, name: 'Vermont', region: 'Northeast' },
    { id: 47, name: 'Virginia', region: 'South' },
    { id: 48, name: 'Washington', region: 'South' },
    { id: 49, name: 'West Virginia', region: 'South' },
    { id: 50, name: 'Wisconsin', region: 'Midwest' },
    { id: 51, name: 'Wyoming', region: 'West' }
  ];

  constructor() {
    this.dataSource = Observable.create((observer: any) => {
      // Runs on every search
      observer.next(this.asyncSelected);
    }).mergeMap((token: string) => this.getStatesAsObservable(token));
  }

  getStatesAsObservable(token: string): Observable<any> {
    let query = new RegExp(token, 'ig');

    return Observable.of(
      this.statesComplex.filter((state: any) => {
        return query.test(state.name);
      })
    );
  }

  changeTypeaheadLoading(e: boolean): void {
    this.typeaheadLoading = e;
  }

  changeTypeaheadNoResults(e: boolean): void {
    this.typeaheadNoResults = e;
  }

  typeaheadOnSelect(e: TypeaheadMatch): void {
    console.log('Selected value: ', e.value);
  }
}

With Template

<pre class="card card-block card-header">Model: {{asyncSelected | json}}
</pre>
  <input [(ngModel)]="asyncSelected"
         [typeahead]="dataSource"
         (typeaheadLoading)="changeTypeaheadLoading($event)"
         (typeaheadNoResults)="changeTypeaheadNoResults($event)"
         (typeaheadOnSelect)="typeaheadOnSelect($event)"
         [typeaheadOptionsLimit]="7"
         typeaheadOptionField="name"
         placeholder="Locations loaded with timeout"
         class="form-control">
  <div *ngIf="typeaheadLoading===true">Loading</div>
  <div *ngIf="typeaheadNoResults===true">&#10060; No Results Found</div>

Here is my attempt at the only part I can't get working:

this.dataSource = Observable.create((observer: any) => {
    // Runs on every search
    observer.next(this.typeAheadResult);
    }).mergeMap((token: string) => {  
        return this.httpClient.get<string[]>(`${this.typeAheadUrl}?q=${token}`);              
    });

Normally, for a call like this I'd do something like:

this.httpClient.get<string[]>(`${this.typeAheadUrl}?q=${token}`)
    .subscribe(results => this.results = results)

But this isn't quite correct

Here is the solution, it probably has some poorly written or redundant code (especially with the .map(r => r) ), that looks off to me, maybe it can be simplified, but to get async results back with HttpClient , this will work:

this.dataSource = Observable.create((observer: any) => {
    // Runs on every search
    observer.next(this.typeAheadResult);
    }).mergeMap((token: string) => {  
        return this.httpClient.get<string[]>(`${this.typeAheadUrl}?q=${token}`).map(r => r);              
    });

I did this:

this.dataSource = Observable.create((observer: Observer<any[]>) => {
        this.httpClient.get<string[]>(`${this.typeAheadUrl}?q=${token}`).subscribe((responseItems: any[]) => {
            observer.next(responseItems);
        });
      });

That works, and it's interesting if you have to check if some conditions are fullfilled to send the request, like, for example, check if the input field is focused. If you don't need to do anything like that, this is simpler:

this.dataSource = this.httpClient.get<string[]>(`${this.typeAheadUrl}?q=${token}`);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM