尧图精选

Angular CDK Bidi 双向文本方向支持全解析:Directionality 服务与 Dir 指令实战指南

🕒 发布时间:2026/9/12 20:35:13 📁 来源:尧图网络
Angular CDK Bidi 双向文本方向支持全解析Directionality 服务与 Dir 指令实战指南【免费下载链接】componentsComponent infrastructure and Material Design components for Angular项目地址: https://gitcode.com/GitHub_Trending/co/componentsbidibidirectional text双向文本是 Angular Component Dev KitCDK中负责 LTR/RTL 布局方向的核心基础设施包。本文以 bidi 官方文档 为主线结合仓库中 directionality.ts、dir.ts、directionality.spec.ts 等源码实现深入讲解如何让组件感知应用当前的文字方向、响应方向切换以及auto值与浏览器原生行为的差异。读完本文你将能够在自己的 Angular 应用中通过注入Directionality或使用Dir指令实现完整的多语言阿拉伯语、希伯来语等 RTL 区域布局适配。一、bidi 包在 CDK 中的定位bidi包位于 src/cdk/bidi它不是一个面向最终用户的 UI 组件而是一套供 CDK 其他组件和业务代码复用的方向感知基础设施。其核心职责对应 bidi.md 开篇的描述是为组件提供一套公共机制用于获取应用 LTR/RTL 布局方向并响应方向的变化。从源码结构看该包由以下文件构成见 public-api.ts文件导出的公共 API职责directionality.tsDirectionality、Direction、_resolveDirectionality方向上下文服务与方向解析逻辑dir.tsDir匹配dir属性的指令bidi-module.tsBidiModuleNgModule 聚合入口dir-document-token.tsDIR_DOCUMENT可注入的 Document 令牌用于测试整个包仅依赖angular/core与angular/common见 BUILD.bazel 中ng_project的deps声明是一个轻量、无副作用的纯基础设施模块。在 CDK 内部Directionality被 overlay、menu、listbox、drag-drop、table、tree、stepper 等多个模块注入使用——这正印证了文档所说部分 CDK 组件如 overlays 和键盘导航需要知道元素处于 RTL 还是 LTR 布局才能正确工作。二、Directionality服务获取当前文字方向2.1 基本用法按照 bidi.md 的说明只要在模块中引入BidiModule任何组件或服务就可以通过构造函数注入Directionality读取当前方向并订阅方向变化import {Component, OnDestroy} from angular/core; import {Directionality} from angular/cdk/bidi; import {Subscription} from rxjs; Component({...}) export class MyWidget implements OnDestroy { /** Whether the widget is in RTL mode or not. */ private isRtl: boolean; /** Subscription to the Directionality change EventEmitter. */ private _dirChangeSubscription Subscription.EMPTY; constructor(dir: Directionality) { this.isRtl dir.value rtl; this._dirChangeSubscription dir.change.subscribe(() { this.flipDirection(); }); } ngOnDestroy() { this._dirChangeSubscription.unsubscribe(); } }要点说明dir.value返回当前的布局方向类型为Direction取值只有ltr | rtl定义见 directionality.tsdir.change是一个EventEmitterDirection在方向发生变化时发出新值。文档原示例中的Subscription.EMPTY是一个惯用写法用于在构造函数中安全地给字段赋初值避免在异步回调触发前出现未初始化订阅。2.2 初始值的来源body 优先于 htmlDirectionality的构造逻辑位于 directionality.tsconstructor() { const _document inject(DIR_DOCUMENT, {optional: true}); if (_document) { const bodyDir _document.body ? _document.body.dir : null; const htmlDir _document.documentElement ? _document.documentElement.dir : null; this.valueSignal.set(_resolveDirectionality(bodyDir || htmlDir || ltr)); } }其取值优先级为body dir...上的dir属性优先html dir...即document.documentElement上的dir属性若两者都未设置则默认ltr。这一点在 directionality.spec.ts 中有明确的测试用例验证当documentElement.dir ltr且body.dir rtl时Directionality.value解析为rtl而body.dir无效值如not-valid会被_resolveDirectionality归一化为ltr见同文件 L59-L66。2.3DIR_DOCUMENT令牌可测试性的设计细心的读者会发现构造函数里注入的是DIR_DOCUMENT而不是 Angular 的DOCUMENT。这一设计的动机记录在 dir-document-token.ts 的注释中单测中不能使用真实的document因为修改真实 DOM 的dir会导致 Safari 下基于几何布局的测试失败也不能直接重新提供 platform-browser 的DOCUMENT因为单元测试代码本身会使用querySelector等 API单独定义令牌是绕开 angular/angular#22559 问题的 workaround该链接为源码注释中引用仅作背景说明。该令牌默认providedIn: root工厂中回退注入DOCUMENT测试时则可像 directionality.spec.ts 那样提供一个假文档TestBed.configureTestingModule({ providers: [{provide: DIR_DOCUMENT, useFactory: () fakeDocument}], });三、Dir指令为子树提供局部方向上下文3.1 核心设计自身即DirectionalityBidiModule导出的第二个重要 API 是Dir指令它匹配任何带有dir属性的元素dir.tsDirective({ selector: [dir], providers: [{provide: Directionality, useExisting: Dir}], host: {[attr.dir]: _rawDir}, exportAs: dir, }) export class Dir implements Directionality, AfterContentInit, OnDestroy { ... }关键在于providers中的{provide: Directionality, useExisting: Dir}Dir指令自己充当Directionality。这意味着任何后代组件注入Directionality时Angular 的依赖注入会向上解析命中最近的Dir实例从而获得最近的祖先方向上下文。沿用文档原示例Dir指令与Directionality服务 API 完全一致——同样暴露value、change、dir输入和dirChange输出因此文档中 2.1 节的注入代码无需任何改动就能自动感知最近祖先的方向。3.2 指令的输入输出Dir指令的完整 API见 dir.ts成员类型说明Input() dirDirection \| auto设置元素方向支持ltr、rtl与autoOutput(dirChange) changeEventEmitterDirection方向变化时发出事件事件名为dirChangevaluegetterDirection解析后的当前方向exportAs: dir—支持模板局部变量引用如#ddir与Directionality服务不同Dir是响应式的当绑定值改变时如[dir]direction()setter 会重新解析并通过change事件通知订阅者dir.ts。注意它只在ngAfterContentInit之后即_isInitialized为 true 时才发出事件避免初始化阶段产生多余的事件流而change流在ngOnDestroy时会被 completedir.tsdirectionality.spec.ts 对两种场景都有断言。一个典型组合示例div [dir]currentDir (dirChange)onDirChanged($event) my-widget/my-widget !-- 内部注入 Directionality 即获得本层方向 -- /div3.3 保留原始属性值Dir的宿主绑定是[attr.dir]: _rawDir其中_rawDir保存的是使用者传入的原始字符串。这意味着即使传入dirautoDOM 上仍然保留dirauto属性但指令的value已被解析为具体的ltr或rtl测试见 directionality.spec.ts。这是一个非常贴心的设计既不影响浏览器对auto的语义又给 CDK 提供了确定性的方向值。此外解析是大小写不敏感的——传入RTL也能正确解析为rtl测试见 directionality.spec.ts。四、auto值的解释CDK 与浏览器的差异4.1 文档声明的差异bidi.md 的第三节明确指出CDK 支持原生dirauto但解释方式与浏览器不同浏览器根据元素的实际文本内容动态推断方向代价较高CDK出于性能考虑通过浏览器的语言设置navigator.language匹配已知的 RTL 语言区域来判断。4.2 源码实现细节该逻辑集中在 directionality.ts 的_resolveDirectionality函数/** Regex that matches locales with an RTL script. Taken from goog.i18n.bidi.isRtlLanguage. */ const RTL_LOCALE_PATTERN /^(ar|ckb|dv|he|iw|fa|nqo|ps|sd|ug|ur|yi|.*-_)(?!.*-_($|-|_))($|-|_)/i; export function _resolveDirectionality(rawValue: string): Direction { const value rawValue?.toLowerCase() || ; if (value auto typeof navigator ! undefined navigator?.language) { return RTL_LOCALE_PATTERN.test(navigator.language) ? rtl : ltr; } return value rtl ? rtl : ltr; }几个值得注意的实现事实RTL 语言清单正则覆盖ar阿拉伯语、ckb中库尔德语、dv迪维希语、he/iw希伯来语、fa波斯语、nqo西非书面文字、ps普什图语、sd信德语、ug维吾尔语、ur乌尔都语、yi意第绪语等语言代码以及Adlm、Arab、Hebr、Nkoo、Rohg、Thaa等 RTL 文字子标签负向前瞻(?!.*-_)用于排除那些虽是 RTL 主语言但明确使用拉丁/西里尔文字的区域如fa-Latn默认回退非rtl的任何值包括auto但navigator.language不可用、无效值一律解析为ltr性能取舍源码注释明确说明之所以用语言匹配而非内容检测是因为基于内容检测可能很昂贵——这正是文档所述for performance reasons的具体含义。4.3 使用建议由于 CDK 对auto的解释与浏览器不同在使用时需要注意如果你的应用或组件主要面向 RTL 语言用户auto基于navigator.language的解析通常已足够准确如果需要按内容级别的精确方向推断应在业务层自行实现例如通过第三方 bidi 检测库并将解析结果显式绑定为ltr或rtl传给Dir/Directionality。五、完整实践一个响应方向的示例组件综合以上内容一个完整的实践示例融合文档示例与源码 API如下import {Component, OnDestroy, OnInit, inject} from angular/core; import {Dir, Directionality} from angular/cdk/bidi; import {Subscription} from rxjs; Component({ selector: app-direction-aware, template: div dirauto !-- 此子树内的 Directionality 均来自最近的 Dir 指令 -- p当前解析方向{{ direction }}/p /div , }) export class DirectionAwareComponent implements OnInit, OnDestroy { private readonly dir inject(Directionality); // 应用级方向 private readonly localDir inject(Dir, {optional: true}); // 最近的 Dir 指令 private changeSub Subscription.EMPTY; direction: ltr | rtl ltr; ngOnInit() { this.direction this.dir.value; this.changeSub this.dir.change.subscribe(dir { this.direction dir; // 在这里执行图标翻转、布局重排等逻辑 }); } ngOnDestroy() { this.changeSub.unsubscribe(); } }注意事项在构造函数中订阅change流是安全的Directionality的初始值在构造阶段已解析完成但文档示例采用的方式同样推荐——把订阅保存在实例字段并在ngOnDestroy中统一清理防止内存泄漏Directionality和Dir都是 Angular 15 信号/现代 DI 架构下的实现valueSignal、inject同时保留了valuegetter 和change事件因此无论是传统构造函数注入还是inject()函数注入均适用见 directionality.ts 与测试组件 InjectsDirectionality。六、与测试相关的行为契约最后directionality.spec.ts 完整覆盖了该模块的行为契约可作为理解实现的辅助材料Directionality优先读取body.dir其次documentElement.dir都未设置时默认ltrL18-L42无效方向值回退为ltrL59-L66、L119-L128Dir提供自身为Directionality后代注入获得最近上下文L70-L79方向改变时发出change/dirChange事件L81-L105销毁时change流 complete防止订阅泄漏L44-L57、L107-L117auto值保留在 DOM 属性上但指令内部解析为具体方向L130-L142解析大小写不敏感L144-L149。对于测试场景CDK 还在 src/cdk/testing/private/fake-directionality.ts 提供了FakeDirectionality等测试替身供编写组件测试时模拟方向变化——如果你需要为业务组件编写方向相关的单元测试可以直接参考该实现。结语bidi包以两个简洁的公共 APIDirectionality服务 Dir指令解决了 Angular 应用中的双向文本方向问题全局方向由Directionality从body/html读取并提供变化通知局部覆盖由Dir指令通过依赖注入天然实现最近祖先方向语义而auto值则基于navigator.language高效解析。无论你是要构建面向中东市场的国际化应用还是想在 CDK 的 overlay、menu、table 等组件基础上做深度定制掌握本文内容后即可直接复用这套成熟的方向感知机制。【免费下载链接】componentsComponent infrastructure and Material Design components for Angular项目地址: https://gitcode.com/GitHub_Trending/co/components创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联 返回资讯列表 →