跳转到内容

Autocomplete 自动补全组件

自动补全是一个普通文本输入框,它通过一组建议的选项来帮助用户输入。

该组件常用于以下两个场景中的单行文本框赋值:

  1. 文本框必须取值于一组预设好的值,例如:一个地址项必须包含一个有效的地址:组合框
  2. 文本框也可以是任何值,但最好能够为用户提供可能的选项,譬如搜索框可以提供近似的或者曾搜索过的选项以节省用户时间:灵活的单文本框

此组件旨在改进 “react-select” 和 “downshift” 这两个包。

Combo box 组合框

必须取值于一个预设的可选数据源。

<Autocomplete
  disablePortal
  id="combo-box-demo"
  options={top100Films}
  sx={{ width: 300 }}
  renderInput={(params) => <TextField {...params} label="Movie" />}
/>

选项结构

默认情况下,该组件接受和以下结构相同的选项:

interface AutocompleteOption {
  label: string;
}
// 或者
type AutocompleteOption = string;

例如:

const options = [
  { label: 'The Godfather', id: 1 },
  { label: 'Pulp Fiction', id: 2 },
];
// or
const options = ['The Godfather', 'Pulp Fiction'];

然而,你也可以通过提供 getOptionLabel 属性来使用不同的结构。

练习

下面的每个示例都是自动完成组件的一个功能点的演示。

选择一个国家

从 248 个国家中选择一个。

可控的状态

此组件有两种可控的状态:

  1. “value” 状态(state)包含了 value/onChange 两种属性的组合。 这个状态表示用户选择的值,如当按下 Enter 键时。
  2. “input value” 状态(state) 则包含了 inputValue/onInputChange 两种属性的组合。 这个状态展示了在文本框中显示的值。
value: 'Option 1'
inputValue: ''

任意输入

Set freeSolo to true so the textbox can contain any arbitrary value.

搜索输入栏

The prop is designed to cover the primary use case of a search input with suggestions, e.g. Google search or react-autowhatever.

自由创造

If you intend to use this mode for a combo box like experience (an enhanced version of a select element) we recommend setting:

  • selectOnFocus 可以帮助用户清除所选定的值。
  • clearOnBlur 可以帮助用户输入一个新值。
  • handleHomeEndKeys 使用HomeEnd 键在弹出窗口内移动焦点。
  • 最后一个选项,例如 加上 "你的搜索结果"

You could also display a dialog when the user wants to add a new value.

分组

You can group the options with the groupBy prop. If you do so, make sure that the options are also sorted with the same dimension that they are grouped by, otherwise, you will notice duplicate headers.

<Autocomplete
  id="grouped-demo"
  options={options.sort((a, b) => -b.firstLetter.localeCompare(a.firstLetter))}
  groupBy={(option) => option.firstLetter}
  getOptionLabel={(option) => option.title}
  sx={{ width: 300 }}
  renderInput={(params) => <TextField {...params} label="With categories" />}
/>

禁用选项

<Autocomplete
  id="disabled-options-demo"
  options={timeSlots}
  getOptionDisabled={(option) =>
    option === timeSlots[0] || option === timeSlots[2]
  }
  sx={{ width: 300 }}
  renderInput={(params) => <TextField {...params} label="Disabled options" />}
/>

useAutocomplete

For advanced customization use cases, a headless useAutocomplete() hook is exposed. It accepts almost the same options as the Autocomplete component minus all the props related to the rendering of JSX. The Autocomplete component is built on this hook.

import { useAutocomplete } from '@mui/base/AutocompleteUnstyled';

The useAutocomplete hook is also reexported from @mui/material for convenience and backward compatibility.

import useAutocomplete from '@mui/material/useAutocomplete';

自定义的 hook

The Godfather

Head to the customization section for an example with the Autocomplete component instead of the hook.

异步请求

The component supports two different asynchronous use-cases:

打开时加载

It displays a progress state as long as the network request is pending.

当你键入内容时进行搜索

If your logic is fetching new options on each keystroke and using the current value of the textbox to filter on the server, you may want to consider throttling requests.

Additionally, you will need to disable the built-in filtering of the Autocomplete component by overriding the filterOptions prop:

<Autocomplete filterOptions={(x) => x} />

Google Maps Places

A customized UI for Google Maps Places Autocomplete. For this demo, we need to load the Google Maps JavaScript and Google Places API.

多个输入值

Also known as tags, the user is allowed to enter more than one value.

Inception
Inception
Inception
Forrest Gump
Inception

固定的选项

In the event that you need to lock certain tags so that they can't be removed, you can set the chips disabled.

Pulp Fiction
Inception

复选框

限制标签数量

You can use the limitTags prop to limit the number of displayed options when not focused.

Inception
Forrest Gump
+1
<Autocomplete
  multiple
  limitTags={2}
  id="multiple-limit-tags"
  options={top100Films}
  getOptionLabel={(option) => option.title}
  defaultValue={[top100Films[13], top100Films[12], top100Films[11]]}
  renderInput={(params) => (
    <TextField {...params} label="limitTags" placeholder="Favorites" />
  )}
  sx={{ width: '500px' }}
/>

尺寸

Fancy smaller inputs? Use the size prop.

Inception
Inception
Inception

个性化

自定义输入

The renderInput prop allows you to customize the rendered input. The first argument of this render prop contains props that you need to forward. Pay specific attention to the ref and inputProps keys.

GitHub 标签选择器

This demo reproduces GitHub's label picker:

help wanted
type: bug

Head to the Customized hook section for a customization example with the useAutocomplete hook instead of the component.

高亮显示

The following demo relies on autosuggest-highlight, a small (1 kB) utility for highlighting text in autosuggest and autocomplete components.

自定义筛选

The component exposes a factory to create a filter method that can be provided to the filterOptions prop. You can use it to change the default option filter behavior.

import matchSorter from 'match-sorter';

const filterOptions = (options, { inputValue }) => matchSorter(options, inputValue);

<Autocomplete filterOptions={filterOptions} />;

createFilterOptions(config) => filterOptions

参数

  1. config (object [optional]):
  • config.ignoreAccents (bool [optional]):默认为 true。 移除字母的变音符号。
  • config.ignoreCase (bool [optional]):默认为 true。 所有字母都小写。
  • config.limit (number [optional]): 默认值为 null。 显示限定数量的建议选项。 例如,如果 config.limit100,,那么只显示前 100 个 匹配的选项。 如果存在很多选项匹配,并且虚拟化设置还没建立成时,这样的限制是非常有效的。
  • config.matchFrom ('any' | 'start' [optional]): 默认值为 'any'
  • config.stringify (func [optional]): 控制如何将一个选项转换成一个字符串,这样,选项就能够和输入文本的片段相匹配。
  • config.trim (bool [optional]):默认为 false。 删除尾随空格。

返回结果

filterOptions: the returned filter method can be provided directly to the filterOptions prop of the Autocomplete component, or the parameter of the same name for the hook.

In the following demo, the options need to start with the query prefix:

const filterOptions = createFilterOptions({
  matchFrom: 'start',
  stringify: (option) => option.title,
});

<Autocomplete filterOptions={filterOptions} />;

进阶使用

For richer filtering mechanisms, like fuzzy matching, it's recommended to look at match-sorter. For instance:

import { matchSorter } from 'match-sorter';

const filterOptions = (options, { inputValue }) => matchSorter(options, inputValue);

<Autocomplete filterOptions={filterOptions} />;

虚拟滚动

Search within 10,000 randomly generated options. The list is virtualized thanks to react-window.

<Autocomplete
  id="virtualize-demo"
  sx={{ width: 300 }}
  disableListWrap
  PopperComponent={StyledPopper}
  ListboxComponent={ListboxComponent}
  options={OPTIONS}
  groupBy={(option) => option[0].toUpperCase()}
  renderInput={(params) => <TextField {...params} label="10,000 options" />}
  renderOption={(props, option) => [props, option] as React.ReactNode}
  // TODO: Post React 18 update - validate this conversion, look like a hidden bug
  renderGroup={(params) => params as unknown as React.ReactNode}
/>

事件

If you would like to prevent the default key handler behavior, you can set the event's defaultMuiPrevented property to true:

<Autocomplete
  onKeyDown={(event) => {
    if (event.key === 'Enter') {
      // Prevent's default 'Enter' behavior.
      event.defaultMuiPrevented = true;
      // your handler code
    }
  }}
/>

设计局限

autocomplete/autofill

Browsers have heuristics to help the user fill in form inputs. However, this can harm the UX of the component.

By default, the component disables the input autocomplete feature (remembering what the user has typed for a given field in a previous session) with the autoComplete="off" attribute. Google Chrome does not currently support this attribute setting (Issue 587466). A possible workaround is to remove the id to have the component generate a random one.

In addition to remembering past entered values, the browser might also propose autofill suggestions (saved login, address, or payment details). In the event you want the avoid autofill, you can try the following:

  • 给输入框一个不同的名字,这样不会给浏览器泄露任何可以滥用的信息。 例如:id="field1" 而不是 id="country"。 若你不填写 id 的话,该组件则会使用一个随机的 id。

  • 设置 autoComplete="new-password"(当设置此属性时,有些浏览器会建议输入高复杂度的密码)。

    <TextField
      {...params}
      inputProps={{
        ...params.inputProps,
        autoComplete: 'new-password',
      }}
    />
    

Read the guide on MDN for more details.

iOS VoiceOver 辅助功能

VoiceOver on iOS Safari doesn't support the aria-owns attribute very well. You can work around the issue with the disablePortal prop.

ListboxComponent

If you provide a custom ListboxComponent prop, you need to make sure that the intended scroll container has the role attribute set to listbox. This ensures the correct behavior of the scroll, for example when using the keyboard to navigate.

无障碍设计

(WAI-ARIA: https://www.w3.org/WAI/ARIA/apg/patterns/combobox/)

We encourage the usage of a label for the textbox. The component implements the WAI-ARIA authoring practices.