I’ve written a small autocomplete custom element. It’s made up of an md-input
and an md-collection
.
What I would like to achieve is that when the user presses the ArrowDown
key the focus moves to to the collection, and the user can use the arrow keys to move up and down the list, finally pressing Enter to commit the selection.
I’ve never worked with keypress events before. I tried to add a keydown.delegate="keydown(event)"
to the md-input
but that just eats anything typed into the input. I’m also not sure how to move the focus to the collection.
The other issue is how to ensure that the collection covers the content below, i.e. setting the z-index.
countries.html
<template>
<style>
.autocomplete .input-field,
.autocomplete .input-field input {
margin-bottom: 0;
}
.autocomplete div.collection {
margin-top: 0;
}
</style>
<div class="autocomplete">
<md-input type="text" label="Country" value.bind="searchText"></md-input>
<md-collection if.bind="showCollection" style="z-index: 1000">
<md-collection-item repeat.for="item of filteredCountries" click.delegate="autocomplete(item)">${item.name}
</md-collection-item>
</md-collection>
</div>
</template>
countries.ts
@autoinject
export class CountriesCustomElement {
@bindable({defaultBindingMode: bindingMode.twoWay}) public value: string;
@observable public state: IState;
public countries: IListItem[] = [];
public filteredCountries: IListItem[] = [];
public showCollection = false;
@observable protected searchText = "";
private selectedCountry: IListItem;
private subscriptions: Subscription[] = [];
constructor(
private store: Store<IState>,
private countriesService: CountriesService
) { }
public async attached() {
this.subscriptions.push(this.store.state.subscribe(state => this.state = state));
await this.countriesService.loadCountries();
}
public detached() {
this.subscriptions.forEach(c => c.unsubscribe());
}
protected stateChanged(state: IState) {
this.countries = state.countries.list;
if (this.value) {
this.selectedCountry = this.countries.find(c => c.id === this.value);
this.searchText = this.selectedCountry.name;
this.showCollection = false;
}
}
protected autocomplete(item: IListItem) {
this.selectedCountry = item;
this.searchText = item.name;
this.showCollection = false;
this.value = item.id;
}
protected searchTextChanged(search: string) {
const value = search.toLowerCase();
if (value.length === 0) {
this.filteredCountries = [];
this.showCollection = false;
} else {
this.filteredCountries = this.countries.filter(c => c.id.toLocaleLowerCase().includes(value) || c.name.toLowerCase().includes(value));
this.showCollection = this.filteredCountries.length > 0;
}
}
}