How to stop Material dropdown autocomplete selection from triggering another search query in Angular 8/9?
I've faced the same issue some time ago, and this is the final solution that I've been using for several inputs in my system.
The steps are:
- Realize that there are actually two events here. Something Typed and Value Selected.
- Create two observables for the the two events and handle them separately.
class Component {
ngOnInit() {
/* SEPARATE THE EVENTS */
// If it's an object, it's been selected.
// I also don't allow selecting `null` but it's up to you.
const itemSelected$ = this.myForm.get('itemName').valueChanges.pipe(
filter(val => typeof val === 'object' && val !== null),
);
// If it's a string, it's been typed into the input
const itemTyped$ = this.myForm.get('itemName').valueChanges.pipe(
filter(val => typeof val === 'string'),
);
/* HANDLE ITEM SELECTED */
itemSelected$.subscribe(item => {
// If you want, you can also handle "fake" items here.
// I use this trick to show a placeholder like "Create New Item" in the dropdown
if (item.id === 0 && item.name === 'Create New Item') {
this.createNewItem().subscribe(
newItem => this.myForm.get('itemName').setValue(newItem),
);
return;
}
// I use this in a custom input component (with ControlValueAccessor)
// So this is where I notify the parent
this.onChange(item);
});
/* HANDLE ITEM TYPED */
const searchQueryResult$ = itemTyped$.pipe(
debounce(200),
tap(value => {/* you could handle starting a loading spinner or smth here */}),
switchMap(name => this.appServiceItem.search({name}, 1)),
);
// now you can either use searchQueryResult$ with async pipe:
// this.filteredResults$ = searchQueryResult$;
// or subscribe to it and update a field in your component class:
searchQueryResult$.subscribe(result => this.searchQueryResult = result);
// If you subscribe, don't forget to clean up subscriptions onDestroy
}
}
I've taken the liberty to add a few suggestions and tricks, but you get the general idea - create two separate mutually exclusive observables and handle them separately.
Made a simple app in Stackblitz, you can check it out. Hope I understood correctly your issue.
Main idea is - when user fills in form input we get string, when autocomplete fills it we get ItemUnit. This way we can decide whether to make http request or not.