news 2026/9/12 16:12:59

Angular Material 自动完成组件 MatAutocomplete 完全指南:API 解析、源码原理与实战示例

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Angular Material 自动完成组件 MatAutocomplete 完全指南:API 解析、源码原理与实战示例

Angular Material 自动完成组件 MatAutocomplete 完全指南:API 解析、源码原理与实战示例

【免费下载链接】componentsComponent infrastructure and Material Design components for Angular项目地址: https://gitcode.com/GitHub_Trending/co/components

自动完成(Autocomplete)是 Angular Material 组件库中最常用的交互组件之一:它由一个普通文本输入框与一个承载候选选项的下拉面板组合而成。本文以本仓库(Component infrastructure and Material Design components for Angular)中@angular/material/autocomplete的官方 API 报告 goldens/material/autocomplete/index.api.md 为主线骨架,结合 组件使用文档 与 核心源码、触发器源码,系统讲解该组件的公开 API 面、底层实现机制、完整配置项与可运行的实战代码。读完本文,你将能够从零搭建带过滤、带分组、可定制挂载位置的自动完成输入框,并理解其键盘交互、无障碍支持与事件模型背后的实现原理。

一、组件家族总览:从 API 报告看整体架构

根据 API 报告,@angular/material/autocomplete包对外暴露的公开 API 由一个 NgModule 和若干组件、指令、注入令牌、事件类与函数组成,它们协同工作构成了完整的自动完成能力:

API 类型名称职责
NgModuleMatAutocompleteModule聚合模块,同时依赖并再导出OverlayModuleMatOptionModuleBidiModuleCdkScrollableModule
组件MatAutocomplete选项面板本身,选择器mat-autocomplete,可导出matAutocomplete
指令MatAutocompleteTrigger绑定在input[matAutocomplete]/textarea[matAutocomplete]上的触发器
指令MatAutocompleteOrigin指定面板的连接锚点元素,选择器[matAutocompleteOrigin]
组件MatOption单个选项,来自../core(由 public-api.ts 再导出)
组件MatOptgroup选项分组,同样来自../core
注入令牌MAT_AUTOCOMPLETE_DEFAULT_OPTIONS覆盖全局默认配置
注入令牌MAT_AUTOCOMPLETE_SCROLL_STRATEGY面板打开期间的滚动策略
注入令牌MAT_AUTOCOMPLETE_VALUE_ACCESSOR将触发器注册为ControlValueAccessor的 provider
事件类MatAutocompleteSelectedEvent选项被选中时触发,携带sourceoption
事件接口MatAutocompleteActivatedEvent活动选项变化时触发,携带sourceoption(可为null
配置接口MatAutocompleteDefaultOptions全局默认选项的类型定义
函数getMatAutocompleteMissingPanelError()生成“找不到面板实例”错误

从依赖关系看,该组件深度复用 CDK 基础设施:@angular/cdk/overlay负责面板浮层定位、@angular/cdk/a11yActiveDescendantKeyManager负责键盘焦点管理、@angular/cdk/scrolling负责滚动容器、@angular/cdk/bidi负责双向文本方向;同时通过ControlValueAccessor@angular/forms的表单体系无缝集成(见 autocomplete-module.ts 与 autocomplete-trigger.ts 中的模块声明)。

二、快速上手:搭建第一个自动完成输入框

使用前先在模块或独立组件中引入MatAutocompleteModule。以下为官方autocomplete-simple示例(源码位于 autocomplete-simple-example.ts 与其 模板)。

组件类:

import {Component} from '@angular/core'; import {FormControl, FormsModule, ReactiveFormsModule} from '@angular/forms'; import {MatAutocompleteModule} from '@angular/material/autocomplete'; import {MatInputModule} from '@angular/material/input'; import {MatFormFieldModule} from '@angular/material/form-field'; @Component({ selector: 'autocomplete-simple-example', templateUrl: 'autocomplete-simple-example.html', styleUrl: 'autocomplete-simple-example.css', imports: [ FormsModule, MatFormFieldModule, MatInputModule, MatAutocompleteModule, ReactiveFormsModule, ], }) export class AutocompleteSimpleExample { myControl = new FormControl(''); options: string[] = ['One', 'Two', 'Three']; }

模板:

<form class="example-form"> <mat-form-field class="example-full-width"> <mat-label>Number</mat-label> <input type="text" placeholder="Pick one" aria-label="Number" matInput [formControl]="myControl" [matAutocomplete]="auto"> <mat-autocomplete #auto="matAutocomplete"> @for (option of options; track option) { <mat-option [value]="option">{{option}}</mat-option> } </mat-autocomplete> </mat-form-field> </form>

两个关键步骤:

  1. 定义面板:用mat-autocomplete标签创建面板,内部用mat-option定义每个选项,[value]决定该选项被选中后写入输入框/表单的值;
  2. 绑定触发器:通过exportAs把面板实例导出到局部模板变量(此处为#auto),再绑定到输入框的matAutocomplete输入属性上。该属性的对应实现是MatAutocompleteTrigger.autocomplete@Input('matAutocomplete')),源码位于 autocomplete-trigger.ts。

组件类中用ReactiveFormsModuleFormControl跟踪输入值。文档也提示:如果偏好模板驱动表单同样可行,只是响应式表单更便于订阅值变化。

三、实现过滤:基于 valueChanges 的自定义过滤器

面板默认在聚焦时可开合、选项可选中,但“边输入边过滤”需要自行实现。官方autocomplete-filter示例(见 autocomplete-filter-example.ts)演示了标准做法:

export class AutocompleteFilterExample { myControl = new FormControl(''); options: string[] = ['One', 'Two', 'Three']; filteredOptions: Observable<string[]>; constructor() { this.filteredOptions = this.myControl.valueChanges.pipe( startWith(''), map(value => this._filter(value || '')), ); } private _filter(value: string): string[] { const filterValue = value.toLowerCase(); return this.options.filter(option => option.toLowerCase().includes(filterValue)); } }

模板中把filteredOptions通过async管道交给mat-option列表:

<mat-autocomplete #auto="matAutocomplete"> @for (option of filteredOptions | async; track option) { <mat-option [value]="option">{{option}}</mat-option> } </mat-autocomplete>

要点解析:

  • startWith('')的作用:用空字符串“预热”值变化流,使组件在初始化时(尚未发生任何输入)就按空串执行一次过滤,从而保证面板在聚焦时即可显示全部选项;
  • 过滤器完全自定义:只要返回MatOption的候选值数组即可,不限于字符串前缀匹配——可以是对象、数组或任意可比较结构;
  • 无障碍提醒:官方文档特别建议,若使用非标准过滤规则(不限于从字符串开头匹配),应在页面上补充说明过滤条件的文字提示,这对使用屏幕阅读器的用户尤其重要。

四、分离控件值与显示值:displayWith 的妙用

默认情况下,mat-option[value]既作为表单保存的控件值,也作为输入框中显示的文本。当两者需要不同时(典型场景:表单保存对象,而输入框只展示其中一个字符串属性),使用MatAutocompletedisplayWith输入属性。

官方autocomplete-display示例(见 autocomplete-display-example.ts):

export interface User { name: string; } export class AutocompleteDisplayExample { myControl = new FormControl<string | User>(''); options: User[] = [{name: 'Mary'}, {name: 'Shelley'}, {name: 'Igor'}]; filteredOptions: Observable<User[]>; constructor() { this.filteredOptions = this.myControl.valueChanges.pipe( startWith(''), map(value => { const name = typeof value === 'string' ? value : value?.name; return name ? this._filter(name as string) : this.options.slice(); }), ); } displayFn(user: User): string { return user && user.name ? user.name : ''; } private _filter(name: string): string[] { const filterValue = name.toLowerCase(); return this.options.filter(option => option.name.toLowerCase().includes(filterValue)); } }

模板中绑定[displayWith]="displayFn"

<mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn"> @for (option of filteredOptions | async; track option) { <mat-option [value]="option">{{option.name}}</mat-option> } </mat-autocomplete>

实现细节:displayWithMatAutocomplete上定义为@Input() displayWith: ((value: any) => string) | null = null(见 autocomplete.ts)。过滤器中的typeof value === 'string' ? value : value?.name判断是必须的——因为当用户回删选中值、输入框里只剩纯字符串时,valueChanges发出的就是字符串而非User对象。

五、强制选择:requireSelection 与全局默认配置

默认情况下,自动完成接受用户随意输入的任何文本。若业务要求“必须从候选中选中一项”,可开启requireSelection输入。其行为(源码注释见 autocomplete.ts)有两方面:

  1. 用户打开面板、改变了输入值但未选择任何选项便离开时,值会被重置为null
  2. 用户打开面板又关闭、且未改动值,则保留旧值。
<mat-autocomplete #auto="matAutocomplete" requireSelection> ... </mat-autocomplete>

该行为可以全局统一配置。MAT_AUTOCOMPLETE_DEFAULT_OPTIONS注入令牌在源码中providedIn: 'root'并带有默认工厂(见 autocomplete.ts):

export const MAT_AUTOCOMPLETE_DEFAULT_OPTIONS = new InjectionToken<MatAutocompleteDefaultOptions>( 'mat-autocomplete-default-options', { providedIn: 'root', factory: () => ({ autoActiveFirstOption: false, autoSelectActiveOption: false, hideSingleSelectionIndicator: false, requireSelection: false, hasBackdrop: false, }), }, );

MatAutocompleteDefaultOptions接口(同样见 autocomplete.ts)支持的全部键为:

配置项类型默认值说明
autoActiveFirstOptionbooleanfalse面板打开时是否高亮第一个选项
autoSelectActiveOptionbooleanfalse键盘导航过程中是否自动选中当前活动选项
requireSelectionbooleanfalse是否强制用户必须做出选择
backdropClassstring应用到遮罩层(backdrop)的 CSS 类
hasBackdropbooleanfalse面板打开时是否显示遮罩层
overlayPanelClassstring \| string[]应用到浮层面板的 CSS 类(可多个)
hideSingleSelectionIndicatorbooleanfalse单选时是否隐藏选中指示图标

应用级覆盖方式,例如在 providers 中:

providers: [ { provide: MAT_AUTOCOMPLETE_DEFAULT_OPTIONS, useValue: {autoActiveFirstOption: true, requireSelection: true}, }, ],

从源码可以看到,组件构造器中正是用这些默认值初始化各输入属性:this.autoActiveFirstOption = !!this._defaults.autoActiveFirstOption,因此组件级输入可以逐项覆盖全局配置(见 autocomplete.ts 构造函数部分)。

六、键盘交互与焦点管理

官方文档给出完整的键盘交互约定:

快捷键行为
Down Arrow移动到下一个选项
Up Arrow移动到上一个选项
Enter选中当前活动选项
Escape关闭面板
Alt+关闭面板
Alt+存在匹配选项时打开面板

这些行为在底层由ActiveDescendantKeyManager实现。面板组件在ngAfterContentInit中创建键盘管理器(见 autocomplete.ts):

this._keyManager = new ActiveDescendantKeyManager<MatOption>(this.options) .withWrap() // 支持首尾循环 .skipPredicate(this._skipPredicate);

值得注意的实现细节:_skipPredicate恒返回false,即不跳过禁用选项。源码注释引用了 WAI-ARIA APG 键盘接口规范——对于 listbox 这类复合控件,禁用的选项仍应可被键盘聚焦(但不可点击),这与普通规则“禁用元素移出 Tab 序”形成例外(见 autocomplete.ts 的_skipPredicate注释)。

与之配套的两个输入属性:

  • autoActiveFirstOption:打开面板即高亮第一个选项,适用于候选较少、希望用户直接回车选中的场景;
  • autoSelectActiveOption:在键盘上下导航过程中,活动选项的值被“自动选中”但暂不写入模型,直到面板关闭才落库;触发器端通过_pendingAutoselectedOption_valueBeforeAutoSelection跟踪此状态(见 autocomplete-trigger.ts)。

七、触发器深入:位置、禁用、滚动策略与常见错误

MatAutocompleteTrigger是连接输入框与面板的枢纽,其选择器为input[matAutocomplete], textarea[matAutocomplete],并通过MAT_AUTOCOMPLETE_VALUE_ACCESSOR(基于NG_VALUE_ACCESSOR)实现ControlValueAccessor(见 autocomplete-trigger.ts),因此可无缝配合FormControl[(ngModel)]formControlName

它的主要输入与公开成员:

成员绑定别名类型/默认值说明
autocompletematAutocompleteMatAutocomplete关联的面板实例
positionmatAutocompletePosition'auto' \| 'above' \| 'below',默认'auto'auto优先在下方展开,视口空间不足时自动翻转到上方;above/below强制固定方向
connectedTomatAutocompleteConnectedToMatAutocompleteOrigin面板的定位锚点,默认是触发器本身
autocompleteAttributeautocomplete'off'透传给原生输入框的autocomplete属性
autocompleteDisabledmatAutocompleteDisabledboolean,默认false禁用后输入框退化为普通输入框,无法打开面板
panelOpen—(getter)boolean面板当前是否打开
activeOption—(getter)MatOption \| null当前活动选项
optionSelectionsObservable<MatOptionSelectionChange>选项选中流
panelClosingActionsObservable<MatOptionSelectionChange \| null>面板关闭动作流
openPanel()/closePanel()/updatePosition()方法编程式控制面板开关与重定位

几点实现层面的细节:

  • 宿主 ARIA 属性自动管理:触发器通过 host 绑定动态维护role="combobox"aria-autocomplete="list"aria-expandedaria-controlsaria-haspopup="listbox"aria-activedescendant等属性,且全部在autocompleteDisabled时置空(见 autocomplete-trigger.ts 的host定义);
  • 滚动策略可替换MAT_AUTOCOMPLETE_SCROLL_STRATEGY令牌默认返回createRepositionScrollStrategy(面板随页面滚动重新定位);若需要固定面板或自定义行为,可注入该令牌提供自己的() => ScrollStrategy
  • 常见错误getMatAutocompleteMissingPanelError()会在“尝试打开一个不存在的mat-autocomplete实例”时被抛出,例如matAutocomplete绑定写错、或试图在ngAfterContentInit钩子之前打开面板——错误信息会提示核对传入的 id 与打开时机。

八、灵活挂载:自定义输入元素与改变面板锚点

8.1 脱离 mat-form-field 的自定义输入

matAutocomplete并不强制要求宿主是mat-form-field。任何input/textarea元素都可以直接挂载触发器,从而完全自定义输入框外观:

<input type="text" [matAutocomplete]="auto" placeholder="Type here"> <mat-autocomplete #auto="matAutocomplete"> @for (option of options; track option) { <mat-option [value]="option">{{option}}</mat-option> } </mat-autocomplete>

官方autocomplete-plain-input示例演示了该用法,适合不想引入mat-form-field全部能力、只想使用自动完成交互的场景。

8.2 把面板挂到其他元素:matAutocompleteOrigin + matAutocompleteConnectedTo

默认面板以输入框为锚点。如需挂载到容器元素(例如自定义包装 div),用matAutocompleteOrigin指令标记锚点,再通过matAutocompleteConnectedTo指向它:

<div class="custom-wrapper-example" matAutocompleteOrigin #origin="matAutocompleteOrigin"> <input matInput [formControl]="myControl" [matAutocomplete]="auto" [matAutocompleteConnectedTo]="origin"> </div> <mat-autocomplete #auto="matAutocomplete"> @for (option of options; track option) { <mat-option [value]="option">{{option}}</mat-option> } </mat-autocomplete>

MatAutocompleteOrigin是一个极简指令,仅暴露自身的ElementRef<HTMLElement>作为连接点(见 autocomplete-origin.ts),connectedTo输入则指向该指令实例。

8.3 面板宽度与外观控制

MatAutocomplete还提供以下与外观相关的输入:

  • panelWidth:任意 CSS 宽度值(如'300px'50),未设置时面板与宿主同宽;
  • class(输入别名):将宿主元素上的类透传到浮层面板内部,便于直接为面板写样式;
  • disableRipple:禁用面板内选项的水波纹反馈;
  • hideSingleSelectionIndicator:隐藏单选时的对勾指示(详见下节无障碍说明)。

九、选项分组:mat-optgroup

当选项数量多、需要分层展示时,用mat-optgroupmat-option分组(官方autocomplete-optgroup示例,位于 src/components-examples/material/autocomplete/autocomplete-optgroup/):

<mat-autocomplete #auto="matAutocomplete"> <mat-optgroup label="Group name"> <mat-option value="item">Item</mat-option> </mat-optgroup> </mat-autocomplete>

MatOptgroup支持label(组标题)与disabled(整组禁用)。面板组件通过@ContentChildren(MAT_OPTGROUP, {descendants: true}) optionGroups收集分组,通过@ContentChildren(MatOption, {descendants: true}) options收集全部选项(见 autocomplete.ts)。源码中还包含一个平台特判:在 Safari 上inertGroups会被置为true(见构造函数),用以规避 VoiceOver 朗读分组时的已知缺陷。

十、无障碍(Accessibility)设计

MatAutocomplete实现了 ARIA combobox 交互模式,这是其无障碍设计的核心:

  • 输入触发器承担role="combobox",弹出内容承担role="listbox"(见面板模板 autocomplete.html);
  • 不要在选项内嵌套交互控件:由于 listbox 模式,选项内部不应再放按钮、复选框等其他可交互元素,否则会干扰绝大多数辅助技术;
  • 必须提供可访问标签:可通过<mat-form-field>内的<mat-label>、原生<label>aria-labelaria-labelledby任一方式给出;
  • 焦点保持在输入框:面板打开时焦点始终留在触发输入框,通过aria-activedescendant指向当前活动选项的 id 来支持选项间导航(面板 id 由_IdGenerator生成mat-autocomplete-前缀的唯一值,见 autocomplete.ts);
  • 选中指示:默认面板用对勾标识已选项。官方文档明确指出,虽然可用hideSingleSelectionIndicator隐藏对勾,但这会降低无障碍性——视觉用户更难(甚至无法)分辨当前选中项,建议仅在确有设计需求时使用。

十一、事件模型与编程控制

MatAutocomplete对外暴露四个事件(定义见 autocomplete.ts):

事件载荷触发时机
optionSelectedMatAutocompleteSelectedEvent(含sourceoption用户选定一个选项
openedvoid面板打开
closedvoid面板关闭
optionActivatedMatAutocompleteActivatedEvent(含sourceoption,可为null活动选项变化(面板打开期间)

典型用法:

<mat-autocomplete #auto="matAutocomplete" (optionSelected)="onSelected($event)" (opened)="onOpened()" (closed)="onClosed()"> ... </mat-autocomplete>
onSelected(event: MatAutocompleteSelectedEvent) { // event.source 为面板实例,event.option 为被选中的 MatOption console.log('selected:', event.option.value); }

与此同时,触发器提供optionSelectionsObservable 与panelClosingActionsObservable 用于更底层的流式监听;openPanel()closePanel()updatePosition()三个方法让业务代码可以精确控制面板的开关与重定位(例如在窗口尺寸变化时手动updatePosition(),不过触发器本身已订阅ViewportRulerBreakpointObserver自动处理了大部分场景,见 autocomplete-trigger.ts)。

附录:核心 API 速查(源自 API 报告)

以下速查依据 goldens/material/autocomplete/index.api.md 整理,供快速引用:

  • MatAutocompleteModule:导入该模块即可使用mat-autocompletematAutocomplete触发器、matAutocompleteOrigin,并自动获得MatOptionModule与 CDK overlay/bidi/scrolling 能力;
  • MatAutocomplete:选择器mat-autocomplete,导出名matAutocomplete;输入含aria-labelaria-labelledbydisplayWithautoActiveFirstOptionautoSelectActiveOptionrequireSelectionpanelWidthdisableRippleclasshideSingleSelectionIndicator;输出含optionSelectedopenedclosedoptionActivated;另有只读成员isOpenpaneloptionsoptionGroupstemplate
  • MatAutocompleteTrigger:选择器input[matAutocomplete], textarea[matAutocomplete];输入matAutocompletematAutocompletePositionmatAutocompleteConnectedToautocomplete(原生属性透传)、matAutocompleteDisabled
  • MatAutocompleteOrigin:选择器[matAutocompleteOrigin],导出名matAutocompleteOrigin
  • 令牌:MAT_AUTOCOMPLETE_DEFAULT_OPTIONSMAT_AUTOCOMPLETE_SCROLL_STRATEGYMAT_AUTOCOMPLETE_VALUE_ACCESSOR
  • 相关测试与更多示例:组件行为可参考 autocomplete.spec.ts,其余示例分布在 src/components-examples/material/autocomplete/ 下的autocomplete-require-selectionautocomplete-auto-active-first-optionautocomplete-plain-inputautocomplete-optgroupautocomplete-harness等目录中,可作为从简单到进阶的完整学习序列。

【免费下载链接】componentsComponent infrastructure and Material Design components for Angular项目地址: https://gitcode.com/GitHub_Trending/co/components

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 16:11:15

MemU:给编码代理装上持久记忆的最快上手路径

MemU&#xff1a;给编码代理装上持久记忆的最快上手路径 【免费下载链接】memU Personal memory across agents 项目地址: https://gitcode.com/GitHub_Trending/mem/memU MemU 是一个面向 Claude Code、Codex 这类编码代理的 AI 记忆系统&#xff1a;会话结束自动沉淀成…

作者头像 李华