I need to call api after typing two letters in input field and it should show me the dropdown, If I type one more letter, it should call another API. I'm having issue in binding and making dynamic API. As of now searchTerm is foo only. I need to change searchTerm each time according to input field
service.ts file:
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Inject, Injectable } from '@angular/core';
import { Client, CLIENT_TOKEN } from 'projects/vhap/src/public-api';
import { BehaviorSubject, Observable, of, throwError } from 'rxjs';
import { delay, distinctUntilChanged } from 'rxjs/operators';
import { Item, Items } from './model';
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
Authorization: 'sometoken'
})
}
@Injectable({ providedIn: 'root' })
export class BetaService {
private readonly searchingsubject = new BehaviorSubject<boolean>(false);
private apiUrl = 'eval/api/Test/GetSearchAutoSuggests'
/**
*
*/
readonly searching$ = this.searchingsubject
.asObservable()
.pipe(distinctUntilChanged());
constructor(
private readonly http: HttpClient,
@Inject(CLIENT_TOKEN) private readonly client: Client
) {}
/**
*
* @param searchTerm
* @param count
* @returns
*/
public getSearchAutoSuggests(
searchTerm: string,
count: number
): Observable<Items> {
const url = `${this.apiUrl}?searchTerm=${searchTerm}&count=${count}`
return this.http.get<Items>(url, httpOptions);
}
}
ts file: //not sure how to use switchMap and bind FormControl
import { Component, OnInit, EventEmitter, Output } from '@angular/core';
import { BetaService } from '../beta.service';
import { FormControl } from '@angular/forms';
import { tap, startWith, debounceTime, distinctUntilChanged, switchMap, map } from 'rxjs/operators';
import { Observable, of, Subscription } from 'rxjs';
@Component({
selector: 'app-find',
templateUrl: './find.component.html',
styleUrls: ['./find.component.scss']
})
export class FindComponent implements OnInit {
myControl = new FormControl();
beta: BetaService;
searchTerm = 'foo';
count = 10;
newData: any = [];
filteredOptions: Observable<any[]>;
constructor(private betaService: BetaService) {
this.beta = betaService;
this.filteredOptions = this.myControl.valueChanges.pipe(
startWith(''),
debounceTime(400),
distinctUntilChanged(),
switchMap(() => {
})
)
}
ngOnInit(): void {
}
showConfig() {
this.betaService.getSearchAutoSuggests(this.searchTerm, this.count)
.subscribe((data) => {
let item = data.Items;
item.map((val) => {
this.newData.push(val);
return this.newData
})
});
}
}
//HTML File
<form >
<mat-form-field >
<input type="text" placeholder="Pick one" aria-label="Number" matInput [formControl]="myControl" [matAutocomplete]="auto">
<mat-autocomplete #auto="matAutocomplete">
<mat-option *ngFor="let option of newData | async" [value]="option">
{{option}}
</mat-option>
</mat-autocomplete>
</mat-form-field>
</form>
CodePudding user response:
this.filteredOptions = this.myControl.valueChanges.pipe(
debounceTime(400),
//ignore search terms that have less than 2 characters, also handling stripping out whitepsaces
filter( (searchTerm: string) => searchTerm.trim().length > 1)
distinctUntilChanged(),
switchMap( (searchTerm: string) => {
// now we switch observables to your service. i.e. "switchmap)
// switchmap handles cancelling the previous pending request for the new one. ensuring the user doesn't see old data as they typehead
return this.betaService.getSearchAutoSuggests(searchTerm, this.count)
})
).subscribe( {
next: (result: Items) => {
// at this point, you have your 'items' array returned.
// you can store it where you like
this.newData = result;
}
})
I observed that your template is using the | async
pipe on the newData
array. and this has a datatype of any
. that is not recommended as it will cause readability issues in the long term.
with the changes above, the newData
array is being updated based on the results of the http request for each keyword search, so you can get rid of the the | async
pipe.
alternative, if you wanted to use the | async
pipe to avoid managing subscriptions in the template, yould need to remove the subscription from your component and modify your template to be
<mat-option *ngFor="let option of filteredOptions | async" [value]="option">
{{option}}
</mat-option>
but this is last note is an observation, not a requirement.