Search bar Autocomplete from API

2020-05-09 13:06发布

问题:

I'm trying to create a book search bar with autocomplete that will show relevant results dynamically with each keypress. Everything is working and I'm getting the data, but results are not showing on the dropdown menu

Using angular 8 and Material 8.2.3

I'm trying to create a book search bar with autocomplete that will show relevant results dynamically with each keypress. Everything is working and I'm getting the data, but results are not showing on the dropdown menu

    import { Component, OnInit } from '@angular/core';
    import { Store } from '@ngrx/store';
    import { AppState } from '../app.state/app.state';
    import { Observable } from 'rxjs/Observable';
    import { UserName } from '../../model/name.model';
    import { FormControl, FormGroup, FormBuilder } from '@angular/forms';
    import { MainService } from '../main.service';
    
    @Component({
      selector: 'app-search',
      templateUrl: './search.component.html',
      styleUrls: ['./search.component.css']
    })
    export class SearchComponent implements OnInit {
      searchForm: FormGroup;
    
      username: Observable<UserName[]>;
      searchTerm: string;
      searchResult: [];
      isLoading = false;
      errorMsg: string;
    
      constructor(private store: Store<AppState>, private searchService: MainService, private formBuilder: FormBuilder) {
        this.username = store.select('username');
       }
    
      ngOnInit() {
        this.searchForm = this.formBuilder.group({
          searchBar: ''
        });
        this.onChanges();
      }
    
      onChanges(): void {
        this.searchForm.get('searchBar').valueChanges.subscribe(val => {
          this.searchService.searchingValue(val).subscribe(function(data){
            this.searchResult = data.items;
          } );
    
        });
    
      }
     
      }

    <!-- <p>{{ username.username }}</p> -->
    <h1>Book Search</h1>
    <form [formGroup]="searchForm">
        
            <!-- <mat-form-field>
                 <input type="text" matInput formControlName="searchBar" [matAutocomplete]="auto">
            </mat-form-field> -->
    
                <mat-form-field>
                    <input matInput placeholder="Search" aria-label="State" [matAutocomplete]="auto"  formControlName="searchBar">
                    <mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn">
                      <mat-option *ngIf="isLoading" class="is-loading">Loading...</mat-option>
                      <ng-container *ngIf="!isLoading">
                        <mat-option *ngFor="let option of searchResult" [value]="option">
                          <span>{{ option.volumeInfo.title }}</span>
    
                        </mat-option>
                      </ng-container>
                    </mat-autocomplete>
                  </mat-form-field>
              
            
    </form>

回答1:

Total working, example you can find out here in this StackBlitz Link

onChange() method you have to pipe () first of all rxjs operators like filter, debounceTime, switchMap. here when text input is changed you can check valueChanges first and then in switchMap() you can fire services api calling of data.

onChanges(){
   this.searchForm.get('searchBar').valueChanges.pipe(
      filter( data => data.trim().length > 0 ),
      debounceTime(500),
      switchMap(  (id: string) => {
         return id ? this.serachService.searchingValue(id.replace(/[\s]/g,'')) : of([]);
      })
   ).subscribe(data =>{
      this.searchResult = data as Array<{}>; 
   })
}

here,

  • first pipe() operator is filter(), used for filter any black spaces entering by user. so, that nothing calls for api requesting.
  • second pipe() operator is debounceTime(500), used for waiting to stop user typing. and exactly after 5ms search string is passed on api request of switchMap()
  • Third pipe() operator is switchMap(), used to switch to a new observable, after every valueChanges.

search.service.ts

searchingValue(value){
    return of(this.bookDetails.filter( booksName => booksName.name.replace(/[\s]/g,'').toLowerCase().indexOf(value.toLowerCase()) === 0 ));
}


回答2:

You can directly pass the created observable to the autocomplete control :

ts

...
    searchResult: Observable;

          onChanges(): void {
                this.searchResult = this.searchForm.get('searchBar').valueChanges.pipe(
                 switchMap(val => this.searchService.searchingValue(val))    
                );

              }
...

html

<h1>Book Search</h1>
<form [formGroup]="searchForm">

        <!-- <mat-form-field>
             <input type="text" matInput formControlName="searchBar" [matAutocomplete]="auto">
        </mat-form-field> -->

            <mat-form-field>
                <input matInput placeholder="Search" aria-label="State" [matAutocomplete]="auto"  formControlName="searchBar">
                <mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn">
                  <mat-option *ngIf="isLoading" class="is-loading">Loading...</mat-option>
                  <ng-container *ngIf="!isLoading">
                    <mat-option *ngFor="let option of searchResult | async" [value]="option">
                      <span>{{ option.volumeInfo.title }}</span>

                    </mat-option>
                  </ng-container>
                </mat-autocomplete>
              </mat-form-field>


</form>