Reputation: 661
I am building an autocomplete function that is querying a backend for suggestions and would like to only get the last query that was made given a certain delay while the user is typing in an angular 5 form control. Currently my code looks like
this.newVendorForm.get('address').valueChanges.pipe(delay(3000)).subscribe(
address => {
this.geocodeApi.getAddressSuggestions(address)
.subscribe(
response => {
console.log('address suggestions');
console.log(response);
this.addressSuggestions = response;
},
error => {
console.log('error getting address suggestion');
console.log(error);
}
)
}
);
This works however it makes a query for each typed in letter after 3000 ms. For example 'test' would query ['t', 'te', 'tes', 'test'] after 3000 ms. How can I just take the last change (i.e. 'test') from valueChanges after the 3000 ms delay and then do the subscribe? Thank you for you help
Upvotes: 1
Views: 2183
Reputation: 191779
What you want is a mixture of debounceTime
and switchMap
.
this.newVendorForm.get('address').valueChanges.pipe(
debounceTime(3000),
switchMap(address => this.geocodeApi.getAddressSuggestions(address).pipe(
catchError(err => {
console.error(err);
return of();
})
)),
filter(Boolean),
).subscribe(response => this.addressSuggestions = response);
debounceTime
makes it so that if there are two valueChanges
emissions within 3 seconds of each other, only the last one is used. This is different than delay
which will emit all of the changes 3 seconds after they were made.switchMap
takes an inner observable such as an http request and changes the observable stream to it -- i.e. you are now subscribed to the getAddressSuggestions
observable stream. If something emits to switchMap
, it will cancel the previous observable. The result of this is that if a previously made getAddressSuggestions
call had not been completed before a new one starts, the previous one is canceled.catchError
(lettable operator version of .catch
) is used on the getAddressSuggestions
observable instead of the valueChanges. Otherwise, if there were an error from the API, the valueChanges observable would complete. Using catchError
where it is allows you to handle the error without completing the valueChanges observable.filter
is used to only emit responses that have values. In case there is an error, the of()
won't be emitted. This is just one way to handle this situation though.Finally you may want to avoid the manual .subscribe
since you will have to .unsubscribe
. Instead you can try to rely on the | async
pipe in your template which will handle the subscription for you.
Upvotes: 5