Reputation: 535
I'm implementing a search box that should make a call to an API with the value the user entered. I want to make the call when the user stops typing for some ms.
This works fine for the first request:
this.searchForm.get('searchQuery').valueChanges.pipe(
filter(data => data.trim().length > 0),
debounceTime(500),
switchMap( (query: string) => this.productsService.searchProducts(query))
).subscribe();
But, if I continue to type it waits for another 500ms and then sends multiple requests (equal to the number of characters typed, for what it seems).
productsChanged = new Subject<Product[]>();
searchProducts(query: string) {
return this.http.get<Product[]>(this.baseUrl + '/products/search/' + query)
.pipe(
tap(products => {
this.products = products;
this.productsChanged.next(this.products.slice());
}));
}
It will probably be something very simple, but I can't seem to understand what's happening.
Stackblitz: https://stackblitz.com/edit/angular-ivy-w4mbhm
Solution
I have found the problem.
I was listening to (ngModelChange):
<input
name="searchQuery"
type="text"
[(ngModel)]="searchQuery"
(ngModelChange)="onSearch()"
formControlName="searchQuery" />
And inside that listener I was appending a new listener to 'valueChanges'. So on each key stroke, a new listener was created.
As expected, a simple mistake, that made me spend some hours.
Thanks for your help!
Upvotes: 0
Views: 1437
Reputation: 71911
Like I suspected, you are subscribing constantly to your form changes. You should only do this once in your component (or not at all, if you use the async
pipe):
ngOnInit(): void {
this.searchForm = new FormGroup({
searchQuery: new FormControl()
});
this.searchForm
.get("searchQuery")
.valueChanges.pipe(
filter(data => data?.trim().length > 0),
debounceTime(500),
switchMap((query: string) => this.productsService.searchProducts(query).pipe(
catchError((e) => of([])
)))
)
.subscribe((e) => {
console.log(e)
});
}
You can also do:
ngOnInit(): void {
this.products$ = this.searchForm.get("searchQuery").valueChanges.pipe(
// ...
)
}
and use the async pipe. This way you don't have to unsubscribe on destroy
Upvotes: 1