DEV Community

jhomegroup
jhomegroup

Posted on

Autocomplete functionality in a Vue.js component using Bootstrap 5

Component Code (AutocompleteInput.vue)

<template>
  <div class="mb-3">
    <label for="autocomplete-input" class="form-label">{{ label }}</label>
    <input
      id="autocomplete-input"
      type="text"
      class="form-control"
      v-model="inputValue"
      @input="onInput"
      @focus="showDropdown = true"
      @blur="onBlur"
      :placeholder="placeholder"
    />
    <ul
      class="dropdown-menu w-100"
      :class="{ show: showDropdown && filteredSuggestions.length }"
    >
      <li
        v-for="(item, index) in filteredSuggestions"
        :key="index"
        @mousedown.prevent="selectSuggestion(item)"
        class="dropdown-item"
      >
        {{ item }}
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: "AutocompleteInput",
  props: {
    label: {
      type: String,
      default: "Autocomplete",
    },
    suggestions: {
      type: Array,
      default: () => [],
    },
    placeholder: {
      type: String,
      default: "Type to search...",
    },
  },
  data() {
    return {
      inputValue: "",
      showDropdown: false,
    };
  },
  computed: {
    filteredSuggestions() {
      const input = this.inputValue.toLowerCase();
      return this.suggestions.filter((item) =>
        item.toLowerCase().includes(input)
      );
    },
  },
  methods: {
    onInput() {
      this.showDropdown = true;
    },
    onBlur() {
      // Delay hiding dropdown to allow click event to register
      setTimeout(() => {
        this.showDropdown = false;
      }, 200);
    },
    selectSuggestion(item) {
      this.inputValue = item;
      this.showDropdown = false;
      this.$emit("selected", item);
    },
  },
};
</script>

<style scoped>
/* Optional: Add custom styles if needed */
</style>

Enter fullscreen mode Exit fullscreen mode

Usage Example

<template>
  <div class="mb-3 position-relative">
    <label for="autocomplete-input" class="form-label">{{ label }}</label>
    <input
      id="autocomplete-input"
      type="text"
      class="form-control"
      v-model="inputValue"
      @input="onInput"
      @focus="showDropdown = true"
      @blur="onBlur"
      :placeholder="placeholder"
      ref="input"
    />
    <ul
      class="dropdown-menu"
      :class="{ show: showDropdown && filteredSuggestions.length }"
      :style="{ width: dropdownWidth + 'px' }"
    >
      <li
        v-for="(item, index) in filteredSuggestions"
        :key="index"
        @mousedown.prevent="selectSuggestion(item)"
        class="dropdown-item"
      >
        {{ item }}
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: "AutocompleteInput",
  props: {
    label: {
      type: String,
      default: "Autocomplete",
    },
    suggestions: {
      type: Array,
      default: () => [],
    },
    placeholder: {
      type: String,
      default: "Type to search...",
    },
  },
  data() {
    return {
      inputValue: "",
      showDropdown: false,
      dropdownWidth: 0, // To dynamically match the input width
    };
  },
  computed: {
    filteredSuggestions() {
      const input = this.inputValue.toLowerCase();
      return this.suggestions.filter((item) =>
        item.toLowerCase().includes(input)
      );
    },
  },
  methods: {
    onInput() {
      this.showDropdown = true;
    },
    onBlur() {
      // Delay hiding dropdown to allow click event to register
      setTimeout(() => {
        this.showDropdown = false;
      }, 200);
    },
    selectSuggestion(item) {
      this.inputValue = item;
      this.showDropdown = false;
      this.$emit("selected", item);
    },
    updateDropdownWidth() {
      // Set the dropdown width dynamically based on the input element
      if (this.$refs.input) {
        this.dropdownWidth = this.$refs.input.offsetWidth;
      }
    },
  },
  mounted() {
    this.updateDropdownWidth();
    window.addEventListener("resize", this.updateDropdownWidth); // Update width on window resize
  },
  beforeUnmount() {
    window.removeEventListener("resize", this.updateDropdownWidth);
  },
};
</script>

<style scoped>
/* Ensure dropdown appears directly below the input field */
.dropdown-menu {
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 1000;
}
</style>

Enter fullscreen mode Exit fullscreen mode

Top comments (0)