Creating a Autocomplete component from Vue JS and typescript
I had to create an Autocomplete component for fun, its no way near perfect. There are plenty of other implementations that are already serving the same purpose – but hey I wanted to do it my self – fair enough? 😉
This is using bootstrap 4 styles, I am using a text box and drop-down menu controls to archive this. Below is the code for the component.
<template> <div> <div class="lookup lookup-right"> <input type="text" class="no-radius" autocomplete="nope" v-bind:class="cssClass" v-bind:placeholder="placeholderText" v-model="value" @change="onChange" @keypress="onChange" @keyup="onChange"> </div> <div class="raw"> <div class="col-12"> <div class="dropdown-menu" x-placement="bottom-start" v-bind:class="{ 'show': open}"> <a v-for="item in items" :key="item" class="dropdown-item" href="#" @click="onClicked(item)">{{item[displayField]}}</a> </div> </div> </div> </div> </template> <script lang="ts"> import Vue from "vue"; import Component from "vue-class-component"; @Component({ props: { cssClass: String, displayField: String, valueField: String, items: Array, placeholderText:String } }) export default class AutocompleteComponent extends Vue { open: boolean = false; value: String = ""; private onClicked(selected: any) { this.open = false; this.value = selected[this.$props["displayField"]]; this.$emit("click", selected); } private onChange() { this.open = this.$props["items"].length > 0 ? true : false; this.$emit("change", this.value); } } </script> <style scoped> .dropdown-menu { position: absolute; top: 0px; left: 0px; will-change: top, left; width: 100%; } .lookup{ width: 100%; } .lookup input{ width: 100%; padding: 5px 12px; } </style>
This requires few improvements, like keyboard navigation on the auto lookup, etc.
But good enough to get started.
Below is the usage.
<template> <div v-bind:class="cssClass"> <autocomplete :cssClass="'form-control'" :items="places" :displayField="'Description'" :valueField="'PlaceId'" :placeholderText="'Search for place ...'" v-model="selection" @change="onChange" @click="onSelect"> </autocomplete> </div> </template> <script lang="ts"> import Vue from "vue"; import Component from "vue-class-component"; import { googlePlacesService } from "../../services/GooglePlacesService"; import autocomplete from "../shared/Autocomplete.vue"; import { toasterService } from "../../services/ToasterService"; import { IRestResponse, IGooglePlaceItem, IGooglePlaceDetails } from "../../shared/models"; @Component({ props: { cssClass: String }, components: { autocomplete } }) export default class AutocompleteSuburbComponent extends Vue { places: IGooglePlaceItem[] = []; selection: string = ""; private onSelect(placeItem: IGooglePlaceItem) { googlePlacesService .get(placeItem.PlaceId) .then((response: IRestResponse<IGooglePlaceDetails>) => { let place: IGooglePlaceDetails = <IGooglePlaceDetails>response.content; this.$emit("click", place); }) .catch(error => { this.$emit("click", null); }); } private onChange(value: any) { googlePlacesService .getAll(value) .then((response: IRestResponse<IGooglePlaceItem[]>) => { this.places = <IGooglePlaceItem[]>response.content; }) .catch(error => { this.places = []; }); } } </script> <style scoped> </style>