Angular Material Expansion 完全指南:mat-expansion-panel 与 mat-accordion 从用法到源码解析
【免费下载链接】componentsComponent infrastructure and Material Design components for Angular项目地址: https://gitcode.com/GitHub_Trending/co/components
mat-expansion-panel是 Angular Material 提供的可展开「详情—摘要」视图组件,用于在有限页面空间内展示可折叠的内容区块;mat-accordion则用于将多个面板组织成手风琴式布局。本文以本仓库官方文档 expansion.md 为主线,结合真实源码与示例,系统讲解面板结构、标题栏定制、操作栏、禁用态、手风琴模式、懒渲染与无障碍实现,帮助你从 API 用法一路深入到组件内部原理。
一、组件总览:面板、标题与手风琴
在 Angular Material 的 Expansion 模块中,主要包含三个可组合的构件:
| 构件 | 选择器 | 职责 |
|---|---|---|
| 展开面板 | <mat-expansion-panel> | 承载可展开/折叠的内容区域,等价于原生<details>的语义 |
| 面板标题 | <mat-expansion-panel-header> | 显示摘要信息,同时是展开/折叠的交互控制区 |
| 手风琴容器 | <mat-accordion> | 把多个面板组织成手风琴,管理同一时刻可展开的面板数量 |
三者对外统一由MatExpansionModule导出,模块定义位于 expansion-module.ts,公共 API 清单见 public-api.ts。使用前在应用中引入该模块(或采用独立组件导入方式)即可。
从组件层级看,MatExpansionPanel继承自 CDK 的CdkAccordionItem(参见 expansion-panel.ts),因此天然获得expanded状态、opened/closed事件以及toggle()/open()/close()编程接口;MatAccordion则继承自CdkAccordion(参见 accordion.ts),负责面板之间的互斥与批量操作。
二、面板内容与 Header 定制
2.1 Header:标题 + 描述
<mat-expansion-panel-header>显示面板内容的摘要,并充当展开/折叠的控制按钮。它内部可包含<mat-panel-title>与<mat-panel-description>两个指令(分别映射到源码中的MatExpansionPanelTitle与MatExpansionPanelDescription,见 expansion-panel-header.ts),用于按 Material Design 规范对齐标题栏内容:
<mat-expansion-panel-header> <mat-panel-title>This is the expansion title</mat-panel-title> <mat-panel-description>This is a summary of the content</mat-panel-description> </mat-expansion-panel-header>上述用法来自官方示例 expansion-overview-example.html。从模板源码看,标题栏由三部分组成:mat-panel-title、mat-panel-description以及一个兜底的ng-content插槽,结构见 expansion-panel-header.html。也就是说,除了标准标题与描述,你还可以直接放入任意自定义内容。
2.2 展开指示图标与 hideToggle
默认情况下,Header 末尾会渲染一个指示展开状态的切换图标(V 形箭头)。该图标由模板中的.mat-expansion-indicator配合内联 SVG 实现(见 expansion-panel-header.html),并通过_showToggle()方法控制是否渲染——该方法在hideToggle或面板disabled时返回false(见 expansion-panel-header.ts)。
如果不需要图标,可通过hideToggle属性隐藏:
<mat-expansion-panel hideToggle> <mat-expansion-panel-header> <mat-panel-title>This is the expansion title</mat-panel-title> <mat-panel-description>This is a summary of the content</mat-panel-description> </mat-expansion-panel-header> <p>This is the primary content of the panel.</p> </mat-expansion-panel>该片段对应示例中的hide-toggle区域(见 expansion-overview-example.html)。hideToggle既可以写在单个面板上,也可以统一写在mat-accordion上作用于其下所有面板——源码中面板的hideTogglegetter 在自身未设置时会回退读取this.accordion.hideToggle(见 expansion-panel.ts)。
2.3 指示图标位置:togglePosition
除了隐藏图标,还可以通过togglePosition将图标放到标题栏的前面(before)或后面(after,默认值),取值为MatAccordionTogglePosition类型(见 accordion-base.ts)。模板中通过_getTogglePosition()动态绑定after/before两个 class(见 expansion-panel-header.ts)。
2.4 自定义 Header 高度
Header 支持expandedHeight与collapsedHeight两个输入,分别指定展开态与折叠态的高度;未设置时返回null,交由样式表默认高度处理(见 expansion-panel-header.ts)。这两个值同样可以像hideToggle一样通过MAT_EXPANSION_PANEL_DEFAULT_OPTIONS注入令牌全局配置(见 expansion-panel.ts)。
三、Action Bar:展开后显示的底部操作区
面板底部可以放置一组「操作行」(action bar),且仅在面板展开时可见。用法如下,出自示例 expansion-steps-example.html:
<mat-expansion-panel [expanded]="step() === 0" (opened)="setStep(0)" hideToggle> <mat-expansion-panel-header> <mat-panel-title>Personal data</mat-panel-title> <mat-panel-description>Type your name and age</mat-panel-description> </mat-expansion-panel-header> <mat-form-field> <mat-label>First name</mat-label> <input matInput /> </mat-form-field> <mat-action-row> <button matButton (click)="nextStep()">Next</button> </mat-action-row> </mat-expansion-panel><mat-action-row>对应源码中的MatExpansionPanelActionRow指令,仅挂载mat-action-rowclass(见 expansion-panel.ts)。在面板模板中它被ng-content select="mat-action-row"匹配并放置在正文之后(见 expansion-panel.html),配合「上一步/下一步」按钮,非常适合做分步表单、向导式流程(expansion-steps示例即演示了这一场景)。
四、禁用面板:disabled
通过disabled属性可以禁用单个面板:
<mat-expansion-panel disabled> <mat-expansion-panel-header> <mat-panel-title>Destination</mat-panel-title> <mat-panel-description>Type the country name</mat-panel-description> </mat-expansion-panel-header> ... </mat-expansion-panel>该片段来自示例中的disabled区域(见 expansion-expand-collapse-all-example.html)。被禁用的面板用户无法手动切换,但仍可通过编程方式操控——因为toggle()等方法的执行路径(MatExpansionPanelHeader._toggle())在disabled时直接返回(见 expansion-panel-header.ts),而面板本身的open()/close()/toggle()方法并不受此限制(见 expansion-panel.ts)。此外,disabled会同步反映为标题栏的aria-disabled,并将tabindex置为-1使其移出键盘 Tab 焦点序列(见 expansion-panel-header.ts)。
五、Accordion 手风琴模式
多个<mat-expansion-panel>放进同一个<mat-accordion>即组成手风琴:
<mat-accordion class="example-headers-align" multi> <mat-expansion-panel> ... </mat-expansion-panel> <mat-expansion-panel> ... </mat-expansion-panel> <mat-expansion-panel> ... </mat-expansion-panel> </mat-accordion>5.1 multi:独立展开状态
multi="true"允许各面板独立展开/折叠,互不影响;multi="false"(默认)则保证同一时刻最多只有一个面板处于展开状态,展开新面板时旧面板自动收起。源码中MatAccordion通过CdkAccordion的互斥逻辑实现这一点,示例见 expansion-expand-collapse-all-example.html 的multi区域。
5.2 displayMode:default 与 flat
mat-accordion支持两种显示模式(见 accordion.ts):
default(默认):展开的面板四周带有类似「沟槽」的间距,使其与其余面板产生不同的高度层级(elevation);flat:所有面板高度一致、无额外间距,呈现扁平外观。
面板的_hasSpacing()方法正是判断「处于展开态且 accordion 的 displayMode 为 default」来决定是否添加mat-expansion-panel-spacingclass(见 expansion-panel.ts),host 绑定见 expansion-panel.ts。
5.3 批量展开/收起与键盘导航
手风琴容器还暴露了编程式 API。在组件模板上通过#accordion引用matAccordion导出名(exportAs: 'matAccordion',见 accordion.ts),即可调用openAll()/closeAll():
<mat-accordion #accordion="matAccordion"> ... </mat-accordion> <div class="example-action-buttons"> <button matButton (click)="accordion.openAll()">Expand All</button> <button matButton (click)="accordion.closeAll()">Collapse All</button> </div>对应示例见 expansion-expand-collapse-all-example.html。此外,MatAccordion内部使用 CDK 的FocusKeyManager管理各面板 Header 的键盘焦点,支持方向键循环移动(withWrap())以及 Home/End 跳转(withHomeAndEnd()),实现见 accordion.ts。
六、懒渲染:ng-template 延迟初始化
默认情况下,即使面板处于折叠状态,其内容也会被初始化。如果希望把内容初始化推迟到面板首次展开时,需要把内容放进<ng-template matExpansionPanelContent>:
<mat-expansion-panel> <mat-expansion-panel-header> This is the expansion title </mat-expansion-panel-header> <ng-template matExpansionPanelContent> Some deferred content </ng-template> </mat-expansion-panel>其底层原理是 Portal 机制:MatExpansionPanelContent指令被@ContentChild捕获(见 expansion-panel.ts),面板通过opened事件流配合startWith(null)、filter(() => this.expanded && !this._portal)与take(1),在首次打开时把模板包成TemplatePortal挂载到ViewContainerRef上(见 expansion-panel.ts)。模板中的cdkPortalOutlet负责最终渲染(见 expansion-panel.html)。
这一特性对内容较重、数量较多的手风琴(如 FAQ、设置项分组)很有价值:折叠状态下不创建内容组件,减少初始渲染开销。
七、可访问性与交互细节
7.1 模拟原生 details/summary
MatExpansionPanel在无障碍层面模拟原生<details>/<summary>的体验。具体实现(见 expansion-panel-header.ts):
- Header 上设置
role="button"; - 通过
aria-controls指向内容区元素的 ID,_getPanelId()返回面板的id; - 动态维护
aria-expanded、aria-disabled属性; - 面板内容区使用
role="region"并带有aria-labelledby指向 Header ID(见 expansion-panel.html),保证屏幕阅读器能正确关联标题与内容; - 面板正文区域在折叠时通过
inert属性从可访问性树中移除(见 expansion-panel.html),避免折叠内容中的控件仍可被聚焦。
7.2 键盘交互
Header 支持键盘操作(见 expansion-panel-header.ts):按Space或Enter键(且未按住修饰键)即切换展开状态;其他方向键事件交由 accordion 的键盘管理器处理,实现面板间的焦点移动。同时,当面板折叠导致内部元素失去焦点时,组件会通过FocusMonitor把焦点程序化地归还给 Header(见 expansion-panel-header.ts)。
7.3 避免嵌套交互控件
不要把按钮、链接等交互控件放进<mat-expansion-panel-header>内部。因为 Header 本身就是按钮语义(role="button"),在其中嵌套交互元素会造成嵌套交互控件,破坏键盘导航与屏幕阅读器的体验,也容易引发事件冲突。工具栏、操作按钮请使用正文内容或mat-action-row承载。
八、状态事件与编程控制
MatExpansionPanel对外提供完整的编程接口与事件(见 expansion-panel.ts):
| 成员 | 类型 | 说明 |
|---|---|---|
expanded | 输入/状态 | 面板是否展开 |
disabled | 输入 | 是否禁用(继承自CdkAccordionItem) |
opened/closed | 事件 | 展开/折叠状态变化(动画前) |
afterExpand/afterCollapse | 输出 | 动画完成后触发 |
toggle()/open()/close() | 方法 | 编程式切换/展开/折叠 |
其中afterExpand/afterCollapse的触发依赖监听transitionend事件并校验grid-template-rows属性变化(见 expansion-panel.ts),即面板正文高度动画通过 CSS Grid 行高过渡实现;当全局动画被禁用(如prefers-reduced-motion或测试环境)时,则退化为直接订阅opened/closed立即派发。示例中「自感知面板」正是通过(opened)/(closed)更新状态文本(见 expansion-overview-example.html)。
九、默认配置全局定制
如果希望项目内所有展开面板共享某套默认值(如统一隐藏切换图标、固定 Header 高度),可以通过MAT_EXPANSION_PANEL_DEFAULT_OPTIONS注入令牌覆盖默认选项(定义见 expansion-panel.ts):
import {MAT_EXPANSION_PANEL_DEFAULT_OPTIONS} from '@angular/material/expansion'; providers: [ { provide: MAT_EXPANSION_PANEL_DEFAULT_OPTIONS, useValue: { collapsedHeight: '40px', expandedHeight: '64px', hideToggle: false, }, }, ],面板构造时会读取该令牌并应用其中的hideToggle(见 expansion-panel.ts),Header 则应用expandedHeight与collapsedHeight(见 expansion-panel-header.ts)。
十、源码结构与进一步阅读
Expansion 模块的源码集中在 src/material/expansion 目录下,核心文件分工如下:
- expansion-panel.ts:面板组件主体,负责状态、懒渲染 Portal、动画事件与默认配置;
- expansion-panel-header.ts:标题栏组件及标题/描述指令,负责交互、焦点与 ARIA 属性;
- accordion.ts:手风琴容器指令,负责互斥、批量操作与键盘导航;
- accordion-base.ts:
displayMode/togglePosition类型与MAT_ACCORDION注入令牌; - expansion-panel.html / expansion-panel-header.html:组件模板;
- testing/expansion-harness.ts:组件测试 Harness,便于在测试中操作面板;
- 行为验证可参考 expansion.spec.ts 与 accordion.spec.ts。
配套的完整可运行示例位于 src/components-examples/material/expansion,覆盖基础用法(expansion-overview)、分步向导(expansion-steps)、批量展开收起(expansion-expand-collapse-all)与测试 Harness 使用(expansion-harness)。
总结
mat-expansion-panel与mat-accordion是一对高复用度的内容收纳组件:单面板负责「摘要 + 详情」的展开折叠,手风琴负责多面板的组织与互斥。通过hideToggle、togglePosition、displayMode、disabled、multi等输入可以灵活定制外观与行为,mat-action-row支持分步式操作流程,matExpansionPanelContent模板指令实现懒渲染,而完整继承原生<details>/<summary>语义的无障碍实现保证了键盘与屏幕阅读器的友好体验。结合本仓库源码阅读,可以更准确地理解每个 API 背后的事件时序、动画机制与可访问性细节,进而在真实项目中用得精准、改得放心。
【免费下载链接】componentsComponent infrastructure and Material Design components for Angular项目地址: https://gitcode.com/GitHub_Trending/co/components
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考