# Autocomplete

The mx-autocomplete component wraps both a text input and a menu to provide autocomplete / typeahead functionality.

# Component-provided filtering

# Basic string filtering

The autocomplete component can perform basic filtering/searching of options if the filterOptions prop is true.

<mx-autocomplete
  label="Search"
  placeholder="Apple, banana..."
  :options.prop="['apple', 'banana', 'pineapple', 'grape']"
  filter-options
  @mxSelect="onSelect"
/>
1
2
3
4
5
6

# Filtering objects by property

It can also render and filter objects. If set to a string, the optionLabel prop specifies which property of the option object should be displayed and used for filtering.

The example below also demonstrates setting the optionIcon prop to an icon class name. See also Setting the label and icon using functions.

<mx-autocomplete
  label="Search"
  placeholder="Apple, banana..."
  :options.prop="[
    { name: 'apple' },
    { name: 'banana' },
    { name: 'pineapple' },
    { name: 'grape' },
  ]"
  filter-options
  option-label="name"
  option-icon="mds-cross"
  @mxSelect="onSelect"
/>
1
2
3
4
5
6
7
8
9
10
11
12
13

# Custom filtering function

More complex filtering can be performed by passing a function for the filterOptions prop. This function should accept the current input value as the first parameter, and an options member as the second.

In the example below, the user can search for "california" or "florida" in addition to "hollywood", "seattle", etc.

I'm also demonstrating how to set the filterOptions property in a separate script (rather than using Vue's .prop modifier), which may be necessary if your host application is using vanillla JavaScript or cannot pass a function to an element from a framework template.

<mx-autocomplete
  class="city-state-autocomplete"
  label="Search"
  placeholder="Hollywood, Miami, Alaska..."
  option-label="city"
  :options.prop="[
    { city: 'Anchorage', state: 'Alaska' },
    { city: 'Austin', state: 'Texas' },
    { city: 'Hollywood', state: 'California' },
    { city: 'Miami', state: 'Florida' },
    { city: 'Orlando', state: 'Florida' },
    { city: 'Sacramento', state: 'California' },
    { city: 'Seattle', state: 'Washington' }
  ]"
  @mxSelect="onSelect"
/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Get a reference to the element, and then set the `filterOptions` property.
const autocomplete = document.querySelector('.city-state-autocomplete');

autocomplete.filterOptions = (inputValue, option) => {
  // Search both the city and state for the input value
  inputValue = inputValue.toLowerCase();
  if (option.city.toLowerCase().includes(inputValue)) return true;
  if (option.state.toLowerCase().includes(inputValue)) return true;
  return false;
};
1
2
3
4
5
6
7
8
9
10

# External filtering

If the matching options need to be fetched from an API call or filtered in the host application, then do not set filterOptions (or set it to false). Instead, attach an input event listener to <mx-autocomplete>, and then update the options prop as needed.

<mx-autocomplete
  label="Search"
  placeholder="Apple, banana..."
  :options.prop="filteredFruits"
  option-label="name"
  @input="searchFruits"
  @mxSelect="onSelect"
/>
1
2
3
4
5
6
7
searchFruits(e) {
  // Show no options if the input is empty
  if (!e.target.value || !e.target.value.trim()) return this.filteredFruits = [];
  const query = e.target.value.trim().toLowerCase();
  // This could just as easily be an API query
  this.filteredFruits = [
    { name: 'apple' },
    { name: 'banana' },
    { name: 'pineapple' },
    { name: 'grape' }
  ].filter(fruit => fruit.name.includes(query));
}
1
2
3
4
5
6
7
8
9
10
11
12

# Set input value on select

The setValueOnSelect prop allows the text input to be automatically set to the selected option's label without having to attach an mxSelect event listener. This allows the autocomplete field to be easily used as a named input in a form.

No matching Beatles found
<mx-autocomplete
  label="Favorite Beatle"
  placeholder="John, Paul..."
  :options.prop="['John', 'Paul', 'George', 'Ringo']"
  filter-options
  set-value-on-select
>
  <span slot="no-results">No matching Beatles found</span>
</mx-autocomplete>
1
2
3
4
5
6
7
8

# Option groups

If a member of the options array is an array itself, it will be rendered as a group. To specify the headings for all groups (prior to filtering), the optionGroups prop should be provided.

<mx-autocomplete
  label="Search"
  placeholder="Apple, banana..."
  :option-groups.prop="['Fruits', 'Vegetables']"
  :options.prop="[
    [
      { name: 'apple' },
      { name: 'banana' },
      { name: 'pineapple' },
      { name: 'grape' }
    ],
    [
      { name: 'lettuce' },
      { name: 'turnip' }
    ]
  ]"
  filter-options
  option-label="name"
  @mxSelect="onSelect"
/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# Custom option rendering

# Setting the label and icon using functions

For the sake of customization, functions may be passed for both the optionLabel and optionIcon props.

An optionLabel function should accept the option as a parameter, and it should return a string, which may contain HTML.

An optionIcon function should also accept the option, and it should return an icon class name (or multiple classes separated by a space).

As a reminder, the examples below leverage Vue's .prop modifier for the sake of brevity. For vanilla JS or other frameworks, you may need to set the element's options, optionLabel, and optionIcon properties in your script. See also Custom filtering function.

<mx-autocomplete
  class="city-state-autocomplete"
  label="City"
  placeholder="Hollywood, Miami, Alaska..."
  :option-label.prop="getCityStateLabel"
  :option-icon.prop="getCityIcon"
  :options.prop="[
    { city: 'Anchorage', state: 'Alaska' },
    { city: 'Austin', state: 'Texas' },
    { city: 'Hollywood', state: 'California' },
    { city: 'Miami', state: 'Florida' },
    { city: 'Orlando', state: 'Florida' },
    { city: 'Sacramento', state: 'California' },
    { city: 'Seattle', state: 'Washington' }
  ]"
  @mxSelect="onSelect"
/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
getCityStateLabel(option) {
  const STATE_ABBR = {
    Alaska: 'AK',
    California: 'CA',
    Florida: 'FL',
    Texas: 'TX',
    Washington: 'WA'
  };
  return `${option.city}, ${STATE_ABBR[option.state]}`;
},

getCityIcon(option) {
  const CAPITALS = ['Austin', 'Sacramento']; // etc.
  return CAPITALS.includes(option.city) ? 'mds-badge-star' : 'ph-bold ph-map-pin';
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# Using a non-block input

The inputTag prop may be used to swap out the default mx-block-input with mx-input, mx-search, or another custom element with a similar interface.

<mx-autocomplete
  input-tag="mx-search"
  placeholder="Search fruits"
  :options.prop="['apple', 'banana', 'pineapple', 'grape']"
  filter-options
  set-value-on-select
  @mxSelect="onSelect"
/>
1
2
3
4
5
6
7

# Advanced example

The following example dynamically changes many of the autocomplete props based on the "Filter by" dropdown.

Company Organization Region Office
<mx-block-wrapper columns="3">
  <mx-autocomplete
    colspan="2"
    label="Search"
    :placeholder="searchPlaceholder"
    :option-groups.prop="searchGroups"
    :options.prop="searchResults"
    :option-icon="searchOptionIcon"
    option-label="name"
    @input="search"
    :value="searchQuery"
    @mxSelect="addSelection"
  />
  <mx-block-select label="Filter by" :value="filterBy" @input="setFilterBy">
    <mx-menu-item>Company</mx-menu-item>
    <mx-menu-item>Organization</mx-menu-item>
    <mx-menu-item>Region</mx-menu-item>
    <mx-menu-item>Office</mx-menu-item>
  </mx-block-select>
</mx-block-wrapper>
<div class="flex flex-wrap gap-4 my-8">
  <mx-chip
    v-for="selection in selections"
    :key="selection.name"
    removable
    :icon="getSelectionIcon(selection.type)"
    @mxRemove="() => removeSelection(selection)"
  >
    {{selection.name}}
  </mx-chip>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
  // ...
  data() {
    return {
      searchQuery: '',
      searchResults: [],
      filterBy: 'Region',
      selections: [],
    };
  },
  computed: {
    searchPlaceholder() {
      return this.filterBy + ' name';
    },
    searchGroups() {
      const plural = this.filterBy === 'company' ? 'companies' : this.filterBy + 's';
      return [plural];
    },
    searchOptionIcon() {
      return this.getSelectionIcon(this.filterBy);
    },
  },
  methods: {
    getSelectionIcon(type) {
      const icons = {
        'Company': 'ph-bold ph-globe',
        'Organization': 'ph-bold ph-tree-structure',
        'Region': 'ph-bold ph-squares-four',
        'Office': 'ph-bold ph-buildings',
      };
      return icons[type];
    },

    setFilterBy(e) {
      this.filterBy = e.target.value;
      this.search();
    },

    search(e) {
      if (e) this.searchQuery = e.target.value?.trim();
      if (!this.searchQuery) return this.searchResults = [];
      // Return fake results (using an array within an array so items are nested under a group heading)
      this.searchResults = [[
        { name: `${this.searchQuery.toUpperCase()} ${this.filterBy}`, type: this.filterBy },
        { name: `Another ${this.searchQuery.toUpperCase()} result`, type: this.filterBy },
        { name: `${this.searchQuery.split('').reverse().join('')}${this.searchQuery}`, type: this.filterBy },
      ]];
    },

    addSelection(e) {
      const selection = e.detail;
      const isAlreadySelected = this.selections.some(s => s.name === selection.name && s.type === selection.type);
      if (isAlreadySelected) return;
      this.selections = [...this.selections, selection];
    },

    removeSelection(selection) {
      this.selections = this.selections.filter(s => s !== selection);
    },
  },
  // ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

# mx-autocomplete

# Properties

Property Attribute Description Type Default
assistiveText assistive-text string undefined
autofocus autofocus boolean false
colspan colspan Used to define the number of columns to span in the parent wrapper if more than 1 number undefined
disabled disabled boolean false
error error boolean false
filterOptions filter-options If not provided (or false), options will not be filtered by the component. If true, options are filtered using String.prototype.includes(). If options is not an array of strings, optionLabel should also be set. If a function ((inputValue, option) => boolean) is passed, that function will be used to filter options against the input value. ((inputValue: string, option: any) => boolean) \| boolean false
inputId input-id The id attribute for the text input string undefined
inputTag input-tag Set to mx-input, etc. to change which element is used as the input. string 'mx-block-input'
label label Text for the label element string 'Search'
maxlength maxlength number undefined
name name The name attribute for the text input string undefined
optionGroups -- An array of group headings if the options are grouped (i.e. options is an array of arrays) string[] []
optionIcon option-icon Class name of icon to show on the left side of each option, or a function (option: any) => string that returns the class name. ((option: any) => string) \| string undefined
optionLabel option-label If options is an array of objects, this specifies which property should be displayed in the menu item. If filterOptions is true, this property will also be used for filtering. string undefined
options -- Array of options, which may be strings or arbitrary objects. any[] undefined
placeholder placeholder Placeholder text for the input string undefined
readonly readonly boolean false
setValueOnSelect set-value-on-select If set to true, the selected label will become the value of the input boolean false
type type The type attribute for the text input string 'search'
value value The current search input value string undefined

# Events

Event Description Type
mxClear Emitted when the clear button is clicked. CustomEvent<void>
mxSelect Emitted when an option is selected. CustomEvent<any>

# Dependencies

# Depends on

# Graph

graph TD;
  mx-autocomplete --> mx-menu-item
  mx-autocomplete --> mx-menu
  mx-menu-item --> mx-checkbox
  style mx-autocomplete fill:#f9f,stroke:#333,stroke-width:4px
1
2
3
4
5

If you are having trouble setting a prop or adding an event listener, refer to this page.