Angular Material List 组件 API 全解析:从 mat-list 到 SelectionList 的完整开发指南
【免费下载链接】componentsComponent infrastructure and Material Design components for Angular项目地址: https://gitcode.com/GitHub_Trending/co/components
本篇技术指南以 Angular Material(co/components 仓库)中@angular/material/list的 API 报告文件为核心骨架,系统讲解<mat-list>、<mat-nav-list>、<mat-action-list>、<mat-selection-list>四大列表变体的组件与指令 API、源码实现原理、无障碍实践以及官方测试 Harness 的完整用法。读完本文,你将掌握列表项内容分区指令(Title/Line/Meta)、选择列表的ControlValueAccessor双向绑定机制、单选/多选切换与hideSingleSelectionIndicator全局配置,并能用 Component Harness 编写可跨测试框架复用的列表测试。
一、文档定位:什么是 API Golden 文件
仓库中的 goldens/material/list/index.api.md 是由 API Extractor,记录@angular/material/list/testing的 Harness 测试 API。
真正的使用文档位于 src/material/list/list.md,实现源码位于 src/material/list 目录。下面我们把三者结合起来,逐层剖析。
二、列表家族总览:四种容器组件
API 报告中清晰给出了四个公开容器组件,它们都继承自内部基类MatListBase(见 src/material/list/list-base.ts):
| 组件 | 选择器 | 用途 | 默认 role |
|---|---|---|---|
MatList | mat-list | 基础列表,纯展示,无行为 | 无(装饰性) |
MatNavList | mat-nav-list | 导航列表,每个项是锚点 | navigation |
MatActionList | mat-action-list | 操作列表,每个项是按钮 | group |
MatSelectionList | mat-selection-list | 选择列表,项为可选中选项 | listbox |
从源码看,四种容器的差异非常精简:
- list.ts 中的
MatList模板仅有<ng-content></ng-content>,MatListBase中_isNonInteractive = true,即默认纯装饰、无任何交互。 - nav-list.ts 与 action-list.ts 都将
_isNonInteractive覆盖为false,让列表项进入"交互模式"——源码注释说明了原因:MDC 规范下交互列表的项只通过键盘可达,而导航/操作列表希望每个项都能用 Tab 键直接聚焦,因此没有继承交互基类,而是让所有项均可通过 Tab 到达。
2.1 基础列表与列表项
最简单的用法是纯文本单行列表:
<mat-list> <mat-list-item>Pepper</mat-list-item> <mat-list-item>Salt</mat-list-item> <mat-list-item>Paprika</mat-list-item> </mat-list>MatListItem的选择器同时支持元素形式和属性形式:mat-list-item, a[mat-list-item], button[mat-list-item](见 list.ts),因此锚点与按钮可以直接复用列表项样式。
2.2 导航列表MatNavList
导航列表用于"每一项都是跳转到其他 URL 的锚点"的场景。简单导航列表可直接在<a>上使用mat-list-item属性:
<mat-nav-list> @for (link of list; track link) { <a mat-list-item href="..." [activated]="link.isActive">{{ link }}</a> } </mat-nav-list>activated是MatListItem的输入属性,用于标记当前激活页(见 list.ts)。它经由coerceBooleanProperty做布尔强制转换,并驱动aria-current属性:当宿主元素是<a>且activated为真时返回'page',否则返回null(见 list.ts),同时通过 host 绑定添加mdc-list-item--activated样式类。
复杂导航列表(如每个条目有多个目标)则把锚点包进<mat-list-item>,再用内容分区指令组织:
<mat-nav-list> @for (link of links; track link) { <mat-list-item [activated]="link.isActive"> <a matListItemTitle href="...">{{ link }}</a> <button matIconButton (click)="showInfo(link)" matListItemMeta> <mat-icon>info</mat-icon> </button> </mat-list-item> } </mat-nav-list>2.3 操作列表MatActionList
每个条目执行某种"动作"的列表使用<mat-action-list>,条目是<button>:
<mat-action-list> <button mat-list-item (click)="save()">Save</button> <button mat-list-item (click)="undo()">Undo</button> </mat-action-list>MatListItemBase构造器(list-base.ts)会自动为没有显式type属性的宿主<button>补上type="button",避免表单提交副作用。
三、列表项内容分区指令(核心 API)
API 报告与 list-item-sections.ts 共同定义了五个内容分区指令,是构建多行列表的关键:
| 指令 | 说明 | 对应 CSS 类 |
|---|---|---|
matListItemTitle | 列表项标题,多行列表必须,全文不换行,每项只能一个 | mdc-list-item__primary-text |
matListItemLine | 列表项内的一行文本,最多两个 | mdc-list-item__secondary-text |
matListItemIcon | 通常置于列表项开头的图标 | mat-mdc-list-item-icon |
matListItemAvatar | 通常置于列表项开头的头像图片 | mat-mdc-list-item-avatar |
matListItemMeta | 在列表项末尾的 meta 区插入内容(图标、按钮等) | mdc-list-item__end |
其中MatListItemIcon与MatListItemAvatar继承自_MatListItemGraphicBase(见 list-item-sections.ts),它根据列表项中复选框/单选钮的位置自动应用mdc-list-item__start或mdc-list-item__end类:在普通列表项中图形默认对齐到开头;在MatListOption中,仅当复选框/单选钮位于末尾(togglePosition === 'after')时图形才对齐到开头。
3.1 多行列表写法
两行列表(标题 + 一行描述):
<mat-list> @for (message of messages; track message) { <mat-list-item> <h3 matListItemTitle>{{message.from}}</h3> <p matListItemLine> <span>{{message.subject}}</span> <span class="demo-2"> -- {{message.content}}</span> </p> </mat-list-item> } </mat-list>三行列表(标题 + 两行描述):
<mat-list> @for (message of messages; track message) { <mat-list-item> <h3 matListItemTitle>{{message.from}}</h3> <p matListItemLine>{{message.subject}}</p> <p matListItemLine class="demo-2">{{message.content}}</p> </mat-list-item> } </mat-list>注意:标题标签类型应按 DOM 层级语义自由选择(不必拘泥于示例中的<h3>)。
3.2 行数推断与lines输入
列表项的行数支持自动推断与显式声明两种方式。list-base.ts 的_inferLinesFromContent按标题数 + 行数 + (有无未分区文本 ? 1 : 0)推断;list-base.ts 的lines输入则通过coerceNumberProperty显式指定行数,可激活文本换行并预留更多空间。按 Material Design 规范,列表项最多支持三行(list-base.ts)。
在开发模式下,list-base.ts 的sanityCheckListItemContent会给出四条一致性警告:一个列表项不能有多个标题;有行文本必须有标题;无标题时不能声明超过一行的换行内容;最多三行。这些检查位于顶层函数中,生产构建可被 Terser 死代码消除。
3.3 图标、头像与 Meta 区
图标列表:使用matListItemIcon:
<mat-list> @for (message of messages; track message) { <mat-list-item> <mat-icon matListItemIcon>folder</mat-icon> <h3 matListItemTitle>{{message.from}}</h3> <p matListItemLine> <span>{{message.subject}}</span> <span class="demo-2"> -- {{message.content}}</span> </p> </mat-list-item> } </mat-list>Meta 区(末尾放置图标或其他内容):使用matListItemMeta:
<mat-list> @for (message of messages; track message) { <mat-list-item> <div matListItemMeta> <mat-icon>folder</mat-icon> </div> <h3 matListItemTitle>{{message.from}}</h3> <p matListItemLine> <span>{{message.subject}}</span> <span class="demo-2"> -- {{message.content}}</span> </p> </mat-list-item> } </mat-list>头像列表:<img matListItemAvatar src="..." alt="...">即可在开头显示头像图片。当既有前导图形又有尾部 meta 时,宿主元素会获得mat-mdc-list-item-both-leading-and-trailing工具类(list.ts),便于样式统一处理。
3.4 分组小标题与分隔线
API 报告中的MatListSubheaderCssMatStyler对应[mat-subheader], [matSubheader]选择器,用于给列表分组加小标题;MatDivider(mat-divider)提供vertical与inset两个布尔输入。组合示例:
<mat-list> <h3 matSubheader>Folders</h3> @for (folder of folders; track folder) { <mat-list-item> <mat-icon matListIcon>folder</mat-icon> <h4 matListItemTitle>{{folder.name}}</h4> <p matListItemLine class="demo-2"> {{folder.updated}} </p> </mat-list-item> } <mat-divider></mat-divider> <h3 matSubheader>Notes</h3> @for (note of notes; track note) { <mat-list-item> <mat-icon matListIcon>note</mat-icon> <h4 matListItemTitle>{{note.name}}</h4> <p matListItemLine class="demo-2"> {{note.updated}} </p> </mat-list-item> } </mat-list>四、选择列表MatSelectionList与MatListOption
选择列表是最复杂的变体,API 报告围绕它列出了最多的公开符号:MatSelectionList、MatListOption、MatSelectionListChange、MatListOptionTogglePosition、SelectionList接口、SELECTION_LIST令牌、MAT_SELECTION_LIST_VALUE_ACCESSOR。
4.1 基本用法与事件
<mat-selection-list>提供一个选择值的界面,每个<mat-list-option>是一个选项:
<mat-selection-list [(ngModel)]="selectedOptions"> <mat-list-option value="1" [selected]="true">Option 1</mat-list-option> <mat-list-option value="2">Option 2</mat-list-option> </mat-selection-list>选项变化通过selectionChange事件发出,载荷为MatSelectionListChange,包含source(源MatSelectionList)与options(发生变化的MatListOption[])两个字段(selection-list.ts)。
重要约束:选择列表的选项内部不应再嵌套任何交互控件(按钮、锚点等),因为整个列表是一个复合组件,其键盘与焦点行为由列表统一管理。
4.2 关键输入属性
MatSelectionList的输入属性(均可从 API 报告与 selection-list.ts 验证):
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
multiple | boolean | true | 是否允许多选;默认多选(显示复选框),设为false切换为单选(显示单选钮)。初始化后不可再修改,否则抛错(selection-list.ts) |
color | ThemePalette | 'accent' | 复选/单选钮的主题色。仅 M2 主题生效,M3 无效果(selection-list.ts) |
compareWith | (o1, o2) => boolean | ===比较 | 判断选项值与选中值是否相等,决定哪些选项显示为选中(selection-list.ts) |
hideSingleSelectionIndicator | boolean | false | 单选模式下是否隐藏指示器。可从MAT_LIST_CONFIG全局配置默认值(selection-list.ts) |
disabled | boolean | false | 禁用整个列表;禁用时所有选项移出 Tab 顺序(tabindex="-1")并设置aria-disabled |
MatListOption的输入属性:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
value | any | — | 选项的值;ngModel/formControl读到的就是选中项的值数组 |
selected | boolean | false | 选中状态,支持双向绑定[(selected)],配合selectedChange事件 |
togglePosition | 'before' \| 'after'(MatListOptionTogglePosition) | 'after' | 复选框/单选钮出现在文本之前还是之后 |
color | ThemePalette | 继承列表的color | 未显式设置时回退到所属列表的颜色(list-option.ts) |
MatListOption还提供toggle()、focus()、getLabel()等公开方法;getLabel()用于列表的 typeahead 打字过滤——优先取标题元素的文本,无标题时回退到未分区文本(list-option.ts)。
4.3 ControlValueAccessor 与表单集成
MatSelectionList实现了ControlValueAccessor(selection-list.ts),通过MAT_SELECTION_LIST_VALUE_ACCESSOR(内部常量NG_VALUE_ACCESSOR提供器)接入 Angular 表单体系,因此可无缝用于ngModel、formControlName与响应式表单:
writeValue(values: string[]):把模型值写回视图,通过compareWith匹配选项并设置选中态(selection-list.ts)。_reportValueChange():收集选中选项的值数组(_getSelectedOptionValues)并回调_onChange,实现视图到模型的同步(selection-list.ts)。setDisabledState(isDisabled):同步表单禁用状态到组件。registerOnChange/registerOnTouched:注册模型更新与失焦回调。
公开的selectAll()/deselectAll()返回发生变化的选项数组;selectedOptions是SelectionModel<MatListOption>,可直接编程操作选中集合。
4.4 键盘交互与焦点管理
MatSelectionList采用 WAI-ARIA 的 listbox 交互模式,通过FocusKeyManager实现漫游 Tabindex(roving tabindex)管理(selection-list.ts):
ArrowUp/ArrowDown在选项间移动焦点;Home/End跳到首/末选项(withHomeAndEnd);- 连续输入字符触发 typeahead 过滤(
withTypeAhead); - 焦点到达边界时循环(
withWrap); Enter/Space切换当前活动选项的选中状态(_toggleOnInteraction,跳过禁用项);- 多选模式下
Ctrl/Cmd + A全选/取消全选(selection-list.ts)。
值得注意的细节:skipPredicate(() => false)表示禁用选项也保留在 Tab 顺序中——源码注释引用了 WAI-ARIA APG 键盘接口实践,明确指出 listbox 中的禁用选项应保持可聚焦(selection-list.ts)。
MatListOption的选中状态与指示器位置联动:多选时在指定位置渲染复选框(_hasCheckboxAt),单选且未隐藏指示器时渲染单选钮(_hasRadioAt),并据此应用mdc-list-item--with-leading-checkbox/radio等 MDC 类(list-option.ts)。
五、全局默认配置:MAT_LIST_CONFIG
API 报告中的MatListConfig接口只有一个可选字段hideSingleSelectionIndicator,配套的MAT_LIST_CONFIG注入令牌定义在 tokens.ts。它的作用是提供列表模块的全局默认选项:
providers: [ {provide: MAT_LIST_CONFIG, useValue: {hideSingleSelectionIndicator: true}}, ]在 list-base.ts 中,MatListBase通过inject(MAT_LIST_CONFIG, {optional: true})注入该配置;MatSelectionList在初始化hideSingleSelectionIndicator时以_defaultOptions?.hideSingleSelectionIndicator ?? false作为默认值(selection-list.ts),实现"全局默认 + 实例覆盖"的两级配置。
另外,API 报告还公开了三个 DI 令牌用于解耦类引用、避免元数据滞留:MAT_LIST(list.ts)、MAT_NAV_LIST(nav-list.ts)、SELECTION_LIST(list-option.ts),后者还用于避免MatListOption与MatSelectionList之间的循环依赖。
六、模块组织:MatListModule
API 报告中的MatListModuleNgModule 声明与导出揭示了依赖关系:模块内部导入了ObserversModule(内容变更观察,用于行数自动更新)、MatRippleModule(波纹反馈)与MatPseudoCheckboxModule(伪复选框),对外导出BidiModule、四类列表、MatDividerModule及所有内容分区指令(index.api.md)。
模块的导入方式:
import {MatListModule} from '@angular/material/list'; @NgModule({ imports: [MatListModule], }) export class MyModule {}七、无障碍实践
官方文档 list.md 对不同列表变体的无障碍要求做了明确划分:
- 导航列表:根元素自动设置
role="navigation",必须通过aria-label或aria-labelledby提供可访问标签。为获得最佳屏幕阅读器体验,建议用<ul>+<li>包裹锚点:
<mat-nav-list aria-label="Select a folder"> <ul> @for (link of list; track link) { <li> <a mat-list-item href="..." [activated]="link.isActive">{{ link }}</a> </li> } </ul> </mat-nav-list>- 操作列表:给
<mat-action-list>添加role="list"与aria-label,并用<li>包裹每个按钮:
<mat-action-list role="list" aria-label="Post actions"> <li> <button mat-list-item (click)="save()">Save</button> </li> <li> <button mat-list-item (click)="undo()">Undo</button> </li> </mat-action-list>选择列表:使用
role="listbox"交互模式,键盘输入与焦点管理全部由组件处理;同样必须提供aria-label或aria-labelledby描述选择内容。文档特别提醒:hideSingleSelectionIndicator会降低可访问性——用户将更难(甚至无法)通过视觉识别选中项,因此默认保持显示单选指示器。自定义场景:默认的
mat-list是纯装饰性的,不设置任何 role、ARIA 属性或键盘快捷键,等同于页面上一组<div>。若用于展示非交互内容列表,应手动为列表添加role="list"、为每个列表项添加role="listitem"。
八、测试 Harness API:@angular/material/list/testing
goldens/material/list/testing/index.api.md 完整记录了官方测试 Harness。它构建在@angular/cdk/testing之上(ComponentHarness、HarnessPredicate、ContentContainerComponentHarness),因此同一套测试代码可运行于 Karma(真实浏览器)与 Protractor 等不同测试环境。
8.1 Harness 一览
| Harness 类 | 对应组件 | 主要方法 |
|---|---|---|
MatListHarness | mat-list | 通过with()过滤,getItems()等 |
MatActionListHarness | mat-action-list | 获取MatActionListItemHarness |
MatNavListHarness | mat-nav-list | 获取MatNavListItemHarness |
MatSelectionListHarness | mat-selection-list | selectItems()、deselectItems()、isDisabled() |
MatListItemHarness | mat-list-item | 文本断言、blur/focus/isFocused |
MatListOptionHarness | mat-list-option | select/deselect/toggle、isSelected、getCheckboxPosition/getRadioPosition |
MatSubheaderHarness | mat-subheader | getText() |
8.2 过滤器(Harness Filters)
列表项过滤器BaseListItemHarnessFilters提供了丰富的文本匹配维度:
interface BaseListItemHarnessFilters extends BaseHarnessFilters { fullText?: string | RegExp; // 完整文本 secondaryText?: string | RegExp | null; tertiaryText?: string | RegExp | null; title?: string | RegExp; text?: string | RegExp; // 已废弃 }ListOptionHarnessFilters额外支持selected?: boolean;NavListItemHarnessFilters额外支持activated?: boolean与href?: string | RegExp | null;SubheaderHarnessFilters支持text?: string | RegExp。
8.3 枚举
MatListItemSection.CONTENT = ".mdc-list-item__content":列表项内容区块的 CSS 选择器,可用于定位文本区域。MatListItemType:单行(ONE_LINE_ITEM = 0)、两行(TWO_LINE_ITEM = 1)、三行(THREE_LINE_ITEM = 2)三种行数类型。
8.4 测试示例
import {MatSelectionListHarness} from '@angular/material/list/testing'; const list = await loader.getHarness(MatSelectionListHarness); const options = await list.getItems({selected: true}); expect(options.length).toBe(1); // 通过文本选中某个选项 await list.selectItems({title: /Pepper/});MatListHarnessBase泛型设计(如MatSelectionListHarness extends MatListHarnessBase<typeof MatListOptionHarness, MatListOptionHarness, ListOptionHarnessFilters>)使得四种列表 Harness 共享同一套"取列表、取选项、过滤"的基础逻辑,只在具体选项类型上分叉。
九、总结
@angular/material/list通过"一个基类 + 四种容器 + 五类内容分区指令"的组合,覆盖了展示、导航、操作、选择四大列表场景。其中选择列表是 API 最密集的组件:ControlValueAccessor表单集成、SelectionModel状态管理、listbox 键盘交互与 roving tabindex 均由组件内置。API golden 文件与源码 src/material/list、官方文档 list.md 三者相互印证,是开发者查阅公共契约、理解实现细节与编写测试的权威依据。更多进阶内容(如 M3 主题下的颜色定制)可继续研读仓库中的主题样式文件 src/material/list/_list-theme.scss 与 src/material/list/_m3-list.scss。
【免费下载链接】componentsComponent infrastructure and Material Design components for Angular项目地址: https://gitcode.com/GitHub_Trending/co/components
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考