Angular @angular/aria/tree:构建符合 ARIA 规范的可访问树形控件完全指南 Angular angular/aria/tree构建符合 ARIA 规范的可访问树形控件完全指南【免费下载链接】angularDeliver web apps with confidence 项目地址: https://gitcode.com/GitHub_Trending/an/angular本文基于 Angular 仓库中adev/src/content/guide/aria/tree.md官方指南系统讲解angular/aria/tree包提供的 Tree、TreeItem、TreeItemGroup 指令如何渲染层级数据、配置单选/多选/导航模式、处理禁用项与焦点策略以及如何使用TreeHarness编写组件测试。读完本文你可以直接复制仓库中的示例代码构建一个键盘导航完备、屏幕阅读器友好的树形控件。概述Tree 组件解决什么问题Tree树形控件用于展示层级数据节点可以展开以显示子节点也可以折叠以隐藏它们。用户通过方向键导航、展开/折叠节点并可选择性地选中条目用于导航或数据选取场景。angular/aria/tree把 W3C ARIA 树形模式APG Treeview Pattern的复杂交互逻辑全部封装进指令开发者只需要声明模板结构即可自动获得完整的键盘导航、焦点管理和 ARIA 语义。在仓库中该组件的使用范例集中位于 tree 示例目录按场景分为nav导航、single-select单选、multi-select多选、single-select-follow-focus选中跟随焦点、disabled-focusable可聚焦的禁用项五个子目录每个场景又提供basic与retro两套视觉实现。适用场景什么时候用 Tree什么时候不用官方指南给出了明确的选型边界适合使用 Tree 的场景构建文件系统导航展示文件夹与文档层级创建嵌套菜单结构显示组织架构图浏览任意层级数据实现带嵌套片段的站点导航应避免使用 Tree 的场景场景应使用的组件指南位置展示扁平列表Listboxlistbox.md展示数据表格Gridgrid.md简单下拉选择Selectselect.md面包屑导航breadcrumb 模式—这一边界的意义在于Tree 自带方向键漫游、Shift 范围选择等交互用在扁平数据上反而增加认知成本。核心功能特性从官方指南声明的特性列表看Tree 组件提供以下能力层级导航嵌套树结构支持展开与折叠选择模式单选或多选支持显式选择explicit或焦点跟随follow-focus两种行为选中跟随焦点焦点变化时自动选中可选项键盘导航方向键、Home、End 键以及 type-ahead按键前缀搜索展开/折叠右/左方向键或 Enter 切换父节点禁用项禁用特定节点并可控其焦点行为焦点模式支持 roving tabindex 或 activedescendant 两种焦点策略RTL 支持面向从右向左书写语言的导航方向适配最小示例模板结构三要素所有示例共享同一套模板结构由三个指令协作完成以 single-select/basic/app.html 为基准ngTree挂在外层ul上构成roletree容器通过模板引用变量#treengTree获取实例ngTreeItem挂在每个li或a上声明[parent]所属容器、[value]业务值、[label]显示文本、[disabled]、[(expanded)]双向绑定展开状态ngTreeItemGroup挂在ul rolegroup内部的ng-template上通过[ownedBy]关联到父ngTreeItem声明哪组子节点归属于哪个节点。子节点通过if (node.children)条件渲染并借助递归ng-template自引用实现任意深度嵌套ul ngTree #treengTree [(value)]selected classbasic-tree ng-template [ngTemplateOutlet]treeNodes [ngTemplateOutletContext]{nodes: nodes, parent: tree} / /ul ng-template #treeNodes let-nodesnodes let-parentparent for (node of nodes; track node.value) { li ngTreeItem [parent]parent [value]node.value [label]node.name [disabled]node.disabled [(expanded)]node.expanded #treeItemngTreeItem span aria-hiddentrue classmaterial-symbols-outlined expand-icon translateno{{ node.children ? chevron_right : }}/span {{ node.name }} span aria-hiddentrue classmaterial-symbols-outlined selected-icon translatenocheck/span /li if (node.children) { ul rolegroup ng-template ngTreeItemGroup [ownedBy]treeItem #groupngTreeItemGroup ng-template [ngTemplateOutlet]treeNodes [ngTemplateOutletContext]{nodes: node.children, parent: group} / /ng-template /ul } } /ng-template注意两个可访问性细节装饰性图标 span 均标记aria-hiddentrue避免屏幕阅读器读出图标字形名每个节点的可见文本{{ node.name }}与[label]绑定保持一致确保辅助技术读取内容与视觉内容一致。对应的数据模型与组件类见 single-select/basic/app.tsimport {Component, signal} from angular/core; import {NgTemplateOutlet} from angular/common; import {Tree, TreeItem, TreeItemGroup} from angular/aria/tree; type TreeNode { name: string; value: string; children?: TreeNode[]; disabled?: boolean; expanded?: boolean; }; Component({ selector: app-root, templateUrl: app.html, styleUrl: app.css, imports: [Tree, TreeItem, TreeItemGroup, NgTemplateOutlet], }) export class App { readonly nodes: TreeNode[] [ { name: public, value: public, children: [ {name: index.html, value: public/index.html}, {name: favicon.ico, value: public/favicon.ico}, {name: styles.css, value: public/styles.css}, ], expanded: true, }, // …… 省略 src、angular.json、package.json 等其余节点 // 完整数据见仓库文件 ]; readonly selected signal([angular.json]); }要点组件以standalone风格直接在imports中声明Tree、TreeItem、TreeItemGroup三个指令选中值用signalstring[]保存通过[(value)]与树双向绑定即使单选场景也是字符串数组统一了 API 心智模型。场景一导航树Navigation Tree导航树用于「点击条目触发动作而非选中」的场景典型如邮件客户端左侧的收件箱/文件夹导航。完整示例见 nav/basic。与普通选择树的差异在容器声明处只多了一个属性ul ngTree #treengTree [nav]true [(value)]selected classbasic-tree设置[nav]true即开启导航模式。此时组件使用aria-current标记当前页而不是用 selection 语义——这是 ARIA 规范对导航类树的要求屏幕阅读器会将其播报为当前所在页。导航树示例还有两个值得注意的写法见 nav/basic/app.html条目使用语义化的a href#...元素而非li并用(click)$event.preventDefault()拦截默认跳转使其成为可访问的原生链接父节点含 children 的节点显式设置[selectable]!node.children即只有叶子节点可被选中符合「导航到具体页面」的语义。场景二单选Single Selection当用户需要从树中选定一项时保持[multi]false默认值即为单选模式。用户按空格键选中当前焦点条目。示例位于 single-select/basic 与 single-select/retro后者为复古视觉风格结构相同。单选模式的交互约定方向键只移动焦点空格确认选中[(value)]信号在选中变更时同步更新。上文「最小示例」中的完整代码即该场景实现。场景三多选Multi-Selection将[multi]true设置到树上即允许多选示例见 multi-select/basicul ngTree #treengTree [multi]true [(value)]selected classbasic-tree多选下的两种操作方式用户按空格逐项选中/取消按住 Shift 配合方向键做范围选择从当前焦点延伸选区。场景四选中跟随焦点Selection Follows Focus在导航类场景中可以让选中自动跟随焦点移动省去每步空格确认。将selectionMode设为follow即可示例见 single-select-follow-focus/basicul ngTree #treengTree [(value)]selected selectionModefollow classbasic-tree此时用户用方向键移动焦点的过程中选中值自动更新。这一模式与「显式选择」默认需空格确认构成指南中提到的 selection mode 两极选型原则是数据选取场景用显式选择页面/记录导航场景用 follow。场景五禁用项Disabled Tree Items通过节点的[disabled]输入禁用特定树节点在上文数据模型中即disabled?: booleansingle-select 示例里src/styles.css节点即被禁用。真正的设计决策在于禁用项能否获得焦点这由树容器上的softDisabled属性控制示例见 disabled-focusable/basic配置行为[softDisabled]true禁用项可以接收焦点但无法被激活或选中软禁用[softDisabled]false键盘导航直接跳过禁用项焦点不会停留选型提示当禁用原因用户需要感知如该文件无权限但你能看到它时用软禁用当禁用项对用户毫无信息量时用硬跳过减少无意义停顿。测试使用 TreeHarness 验证树行为Angular Aria 为 Tree 提供了组件 harness可脱离 DOM 断言层级结构与交互效果。官方指南给出的完整测试示例如下引用自 tree.mdimport {ComponentFixture, TestBed} from angular/core/testing; import {HarnessLoader} from angular/cdk/testing; import {TestbedHarnessEnvironment} from angular/cdk/testing/testbed; import {TreeHarness} from angular/aria/tree/testing; import {MyTreeComponent} from ./my-tree; // Your component describe(MyTreeComponent, () { let fixture: ComponentFixtureMyTreeComponent; let loader: HarnessLoader; beforeEach(async () { TestBed.configureTestingModule({ imports: [MyTreeComponent], }); fixture TestBed.createComponent(MyTreeComponent); await fixture.whenStable(); loader TestbedHarnessEnvironment.loader(fixture); }); it(should navigate and expand tree items, async () { const tree await loader.getHarness(TreeHarness); // Get top-level structure representation expect(await tree.getTreeStructure()).toEqual({ children: [{text: public}, {text: src}, {text: package.json}], }); // Get all items (currently visible) const items await tree.getItems(); expect(items.length).toBe(3); // Expand the first item (public) expect(await items[0].isExpanded()).toBe(false); await items[0].click(); expect(await items[0].isExpanded()).toBe(true); // Verifying tree structure updates after expansion expect(await tree.getTreeStructure()).toEqual({ children: [ { text: public, children: [{text: index.html}, {text: styles.css}], }, {text: src}, {text: package.json}, ], }); }); });从该测试可以读出TreeHarness的核心 API 面getTreeStructure()返回整棵树的嵌套结构快照文本 children是断言层级正确性的主力方法getItems()获取当前可见的条目折叠节点的子级不在其中因此展开前后条目数量会变化单个 item 提供isExpanded()、click()等方法可模拟用户展开操作后再复查结构。harness 基于angular/cdk/testing的HarnessLoader体系配合TestbedHarnessEnvironment.loader(fixture)即可在TestBed中获取。API 参考与延伸阅读指南列出的三个核心 API其在线文档页位于 Angular 官方站点仓库内的 API 元数据快照可见 aria-tree.jsonTree— 树容器指令ngTree承载multi、nav、selectionMode、softDisabled、value等输入TreeItem— 树节点指令ngTreeItem承载parent、value、label、disabled、expanded、selectable等输入TreeItemGroup— 子节点组指令ngTreeItemGroup通过ownedBy将一组子节点挂靠到父节点。同系列指南还包括 accordion.md、grid.md、listbox.md、menubar.md、select.md、tabs.md 等组件可对照选型总览见 overview.md。小结angular/aria/tree的价值在于把 ARIA 树形模式中容易出错的细节——方向键漫游、Shift 范围选择、展开/折叠焦点回退、aria-current与 selection 语义的区分、禁用项焦点策略——全部内建到三个轻量指令里。开发者只负责提供nodes数据与递归模板即可获得一个键盘与屏幕阅读器双友好的层级控件配合TreeHarness这些行为还能被单元测试逐条锁定。【免费下载链接】angularDeliver web apps with confidence 项目地址: https://gitcode.com/GitHub_Trending/an/angular创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考