構(gòu)到扁平/嵌套數(shù)據(jù)源實現(xiàn))
深入解讀 Angular Material Tree 公共 API從指令結(jié)構(gòu)到扁平/嵌套數(shù)據(jù)源實現(xiàn)【免費(fèi)下載鏈接】componentsComponent infrastructure and Material Design components for Angular項目地址: https://gitcode.com/GitHub_Trending/co/componentsmat-tree是 Angular Material 提供的數(shù)據(jù)層級展示組件它建立在 CDK Tree 的基礎(chǔ)上用 Material Design 風(fēng)格封裝了一套完整、可無障礙訪問的樹形控件。本篇文章以倉庫中由 API Extractor 自動生成的 goldens/material/tree/index.api.md 為骨架結(jié)合 src/material/tree 的源碼實現(xiàn)與 src/material/tree/tree.md 使用文檔逐項解析angular/material_tree的完整公開 API組件、指令、數(shù)據(jù)源、模塊聲明以及l(fā)evelAccessor/childrenAccessor帶來的新一代無障礙實現(xiàn)路徑。讀完后你將能準(zhǔn)確理解每個公開類型的能力邊界與棄用狀態(tài)并能在自己的 Angular 項目中正確選用扁平樹或嵌套樹方案。一、API 報告概覽一個包裹 CDK Tree 的 Material 封裝層angular/material_tree的 API 報告由 API Extractor 自動生成文件頭明確標(biāo)注 Do not edit this file. It is a report generated by API Extractor它精確刻畫了包的公開導(dǎo)出面。從報告的 import 列表可以看出整個 Material Tree 的幾乎所有功能都來源于對 CDK 樹形基礎(chǔ)設(shè)施的復(fù)用CdkTree、CdkTreeNode、CdkNestedTreeNode、CdkTreeNodeDef、CdkTreeNodePadding、CdkTreeNodeToggle、CdkTreeNodeOutlet均來自angular/cdk/treeDataSource、CollectionViewer來自angular/cdk/collectionsFlatTreeControl、TreeControl來自angular/cdk/tree運(yùn)行期還依賴angular/cdk/bidiBidiModule用于 RTL 布局支持。報告的 public-api.ts 將 7 個源文件全部重新導(dǎo)出node、padding、tree、tree-module、toggle、outlet以及data-source下的扁平/嵌套兩個數(shù)據(jù)源。這 7 個模塊對應(yīng)了報告中的 8 個公開類公開類聲明位置繼承自職責(zé)MatTreetree.tsCdkTree樹容器組件mat-treeMatTreeNodenode.tsCdkTreeNode扁平樹節(jié)點mat-tree-nodeMatNestedTreeNodenode.tsCdkNestedTreeNode嵌套樹節(jié)點mat-nested-tree-nodeMatTreeNodeDefnode.tsCdkTreeNodeDef節(jié)點模板定義[matTreeNodeDef]MatTreeNodePaddingpadding.tsCdkTreeNodePadding層級縮進(jìn)[matTreeNodePadding]MatTreeNodeToggletoggle.tsCdkTreeNodeToggle展開/收起開關(guān)[matTreeNodeToggle]MatTreeNodeOutletoutlet.tsCdkTreeNodeOutlet子節(jié)點渲染出口[matTreeNodeOutlet]MatTreeModuletree-module.ts—NgModule 聚合聲明這種“薄封裝”的設(shè)計意味著Material Tree 與 CDK Tree 使用完全相同的接口與數(shù)據(jù)流區(qū)別只在于選擇器前綴由cdk-換成mat-并疊加了 Material 的視覺樣式與無障礙增強(qiáng)。二、MatTree組件樹的容器MatTreeT, K T是樹的容器組件繼承CdkTreeT, K。其源碼tree.ts揭示了幾個關(guān)鍵實現(xiàn)細(xì)節(jié)選擇器與導(dǎo)出名selector: mat-treeexportAs: matTree模板ng-container matTreeNodeOutlet/ng-container即根節(jié)點由樹模板中的matTreeNodeOutlet出口渲染樣式與封裝styleUrl: tree.cssencapsulation: ViewEncapsulation.None類名為mat-tree變更檢測注釋明確指出沿用CdkTree的默認(rèn)變更檢測策略ChangeDetectionStrategy.Eager因為樹的數(shù)據(jù)流復(fù)雜不適合 OnPush 下的局部更新假設(shè)DI 提供providers: [{provide: CdkTree, useExisting: MatTree}]——這是理解整個封裝的鑰匙任何通過CdkTree注入的依賴在mat-tree中都會解析到MatTree實例CDK 的內(nèi)部邏輯原封不動地跑在 Material 組件上。組件內(nèi)部只聲明了一個ViewChild(MatTreeNodeOutlet, {static: true}) _nodeOutlet字段作為數(shù)據(jù)節(jié)點插入的出口引用。三、三種節(jié)點指令模板、扁平節(jié)點與嵌套節(jié)點3.1MatTreeNodeDef節(jié)點模板定義[matTreeNodeDef]指令node.ts用于捕獲一個節(jié)點的模板并支持matTreeNodeDefWhen謂詞對應(yīng) CDK 的when輸入當(dāng)數(shù)據(jù)節(jié)點滿足條件時選擇該模板。它還暴露了一個matTreeNode輸入把節(jié)點數(shù)據(jù)導(dǎo)出到模板上下文中供模板內(nèi)的綁定使用mat-tree-node *matTreeNodeDeflet node {{node.key}}: {{node.value}} /mat-tree-node同一個樹中可以存在多個節(jié)點模板運(yùn)行時按when謂詞逐條匹配tree.md 中的“Conditional template”示例展示了如何為特殊節(jié)點渲染不同外觀。3.2MatTreeNode扁平樹節(jié)點mat-tree-node是扁平樹的節(jié)點元素繼承CdkTreeNode。其 host 綁定node.ts集中體現(xiàn)了無障礙與交互邏輯[attr.aria-expanded]動態(tài)反映節(jié)點的展開狀態(tài)[attr.aria-level]level 1用層級渲染出 ARIA level[attr.aria-posinset]/[attr.aria-setsize]節(jié)點在兄弟集合中的位置與集合大小(click)_focusItem()點擊時聚焦節(jié)點[tabindex]_getTabindexAttribute()由TreeKeyManager統(tǒng)一管理焦點。API 報告中標(biāo)記的兩個棄用成員也在這段源碼里得到印證deprecatedbreaking-change 21.0.0移除tabIndexInputBinding別名tabIndex源碼注釋說明默認(rèn)情況下MatTreeNode通過TreeKeyManager管理焦點直接設(shè)置 tabIndex 會讓鍵盤管理器進(jìn)入意外狀態(tài)因此建議避免使用node.tsdisableddisabled只是isDisabled的別名源碼注釋同樣標(biāo)記為 21.0.0 移除node.ts。另外ngAcceptInputType_disabled與ngAcceptInputType_tabIndexInputBinding兩個靜態(tài)字段是 Angular 編譯器生成的類型收窄標(biāo)記用于把模板中的字符串輸入安全轉(zhuǎn)換為boolean/number。3.3MatNestedTreeNode嵌套樹節(jié)點mat-nested-tree-node用于嵌套樹繼承CdkNestedTreeNode并實現(xiàn)了AfterContentInit、OnInit、OnDestroy三個生命周期接口。它與扁平節(jié)點最大的區(qū)別是子節(jié)點在 DOM 中直接嵌套在父節(jié)點內(nèi)部因此父節(jié)點的模板必須包含一個matTreeNodeOutlet出口mat-nested-tree-node *matTreeNodeDeflet node {{node.value}} ng-container matTreeNodeOutlet/ng-container /mat-nested-tree-node源碼中還值得注意 DI 提供關(guān)系node.tsMatNestedTreeNode同時把自己注冊為CdkNestedTreeNode、CdkTreeNode與CDK_TREE_NODE_OUTLET_NODE這使得嵌套節(jié)點的子節(jié)點出口能拿到父節(jié)點引用。3.4 生命周期鉤子重寫的來歷MatTreeNode和MatNestedTreeNode都重寫了ngOnInit/ngAfterContentInit/ngOnDestroy并僅調(diào)用super。源碼注釋解釋了原因這是對 Angular 兩個歷史 issue#23091、#19145的規(guī)避——AOT 編譯下父類的生命周期鉤子不會被自動調(diào)用因此需要子類顯式橋接。四、交互與布局指令Toggle、Padding、Outlet4.1MatTreeNodeToggle展開/收起開關(guān)[matTreeNodeToggle]指令toggle.ts是CdkTreeNodeToggle的空包裝暴露matTreeNodeToggleRecursive輸入。將其附著在按鈕上點擊或鍵盤激活即可觸發(fā)樹的展開/收起設(shè)為true時遞歸展開/收起整棵子樹mat-tree-node *matTreeNodeDeflet node button matTreeNodeToggle aria-labeltoggle tree node [matTreeNodeToggleRecursive]true mat-iconexpand/mat-icon /button {{node.value}} /mat-tree-node文檔特別提醒toggle 應(yīng)掛在button元素上以保證鍵盤可達(dá)若使用圖標(biāo)按鈕必須提供aria-labeltree.md Adding expand/collapse 一節(jié)。4.2MatTreeNodePadding扁平樹專屬縮進(jìn)[matTreeNodePadding]padding.ts僅用于扁平樹因為扁平樹的所有節(jié)點在 DOM 中是同級兄弟無法用 CSS 的嵌套結(jié)構(gòu)表達(dá)層級必須靠縮進(jìn)呈現(xiàn)深度level別名matTreeNodePadding節(jié)點深度源碼注釋明確說明padding 為level * indent像素并通過numberAttribute做輸入轉(zhuǎn)換indent別名matTreeNodePaddingIndent每級縮進(jìn)量默認(rèn) 40px注釋注明取自 Material Design 菜單子菜單規(guī)范。嵌套樹不需要該指令——它的 DOM 天然嵌套縮進(jìn)直接用 CSS 實現(xiàn)即可。4.3MatTreeNodeOutlet子節(jié)點渲染出口[matTreeNodeOutlet]outlet.ts實現(xiàn)了CdkTreeNodeOutlet通過inject(ViewContainerRef)拿到出口位置的視圖容器并把CDK_TREE_NODE_OUTLET_NODE即宿主嵌套節(jié)點可選注入_node字段。它在MatTree模板與嵌套節(jié)點模板中標(biāo)記子節(jié)點的插入位置是嵌套樹 DOM 結(jié)構(gòu)成立的基礎(chǔ)。五、數(shù)據(jù)源扁平化與嵌套數(shù)據(jù)的兩種接入方式5.1MatTreeNestedDataSourceT嵌套數(shù)據(jù)源嵌套數(shù)據(jù)源nested-data-source.ts內(nèi)部用一個BehaviorSubjectT[]持有根節(jié)點數(shù)組。connect(collectionViewer)合并viewChange與數(shù)據(jù)流直接返回this.data。它的設(shè)計哲學(xué)在注釋中寫得很清楚嵌套數(shù)據(jù)源不需要考慮扁平化也不需要處理展開/收起的數(shù)據(jù)重組——這些交給TreeControl和各非葉節(jié)點即可。5.2MatTreeFlattenerT, F, K扁平化引擎MatTreeFlattenerflat-data-source.ts負(fù)責(zé)把嵌套結(jié)構(gòu)T轉(zhuǎn)換為帶層級信息的扁平結(jié)構(gòu)F。構(gòu)造器接收四個函數(shù)參數(shù)類型作用transformFunction(node: T, level: number) F節(jié)點轉(zhuǎn)換附加expandable、level等字段getLevel(node: F) number讀取扁平節(jié)點的層級isExpandable(node: F) boolean判斷節(jié)點是否可展開getChildren(node: T) ObservableT[] \| T[] \| undefined \| null取出節(jié)點的子節(jié)點支持同步數(shù)組或 Observable其核心算法是三個方法_flattenNode先變換當(dāng)前節(jié)點并壓入結(jié)果數(shù)組若可展開則取出子節(jié)點同步數(shù)組直接遞歸Observable 則pipe(take(1))訂閱一次后再遞歸flat-data-source.ts_flattenChildren遍歷子節(jié)點并維護(hù)parentMap記錄每層是否是最后一個兄弟供樹狀連線等場景使用flat-data-source.tsexpandFlattenedNodes結(jié)合TreeControl的展開狀態(tài)把扁平節(jié)點過濾成當(dāng)前可見列表flat-data-source.ts。文件頭部的注釋給出了一個直觀示例{key: Fruits, children: [...]}會被展開成{key: Fruits, expandable: true, level: 1}、{key: Apple, expandable: false, level: 2}等扁平節(jié)點。5.3MatTreeFlatDataSourceT, F, K扁平數(shù)據(jù)源MatTreeFlatDataSourceflat-data-source.ts在內(nèi)部維護(hù)三個BehaviorSubject_data、_flattenedData、_expandedData設(shè)置data時依次觸發(fā)更新_data→ 用 flattener 生成全量扁平節(jié)點 → 同步到treeControl.dataNodesflat-data-source.tsconnect合并三個信號源collectionViewer.viewChange滾動/視圖變化、treeControl.expansionModel.changed展開狀態(tài)變化、_flattenedData數(shù)據(jù)變更每次重算可見節(jié)點并推送給樹flat-data-source.ts。這套機(jī)制的優(yōu)點是滾動友好由于輸出永遠(yuǎn)是單層數(shù)組可以無縫配合虛擬滾動等場景。API 報告與源碼都將MatTreeFlattener、MatTreeFlatDataSource標(biāo)記為deprecated建議改用childrenAccessor方式預(yù)計 21.0.0 移除。5.4 新一代接入方式levelAccessor與childrenAccessortree.md 明確給出了兩種推薦的接入方式levelAccessor傳入一個函數(shù)給定數(shù)據(jù)項返回其所在層級。數(shù)據(jù)源輸出的是已扁平化的單數(shù)組數(shù)據(jù)源需要監(jiān)聽(expansionChange)事件并在展開/收起時重新提供可見節(jié)點數(shù)組childrenAccessor傳入一個函數(shù)給定數(shù)據(jù)項返回其子節(jié)點。此時數(shù)據(jù)源只需提供根節(jié)點數(shù)組樹的層級關(guān)系由該訪問器即時解析trackBy與ngFor的trackBy類似告訴樹如何唯一標(biāo)識節(jié)點用于在數(shù)據(jù)更新時高效復(fù)用 DOM 節(jié)點mat-tree [dataSource]dataSource [treeControl]treeControl [trackBy]trackByFn5.5 無障礙Accessibilitymat-tree實現(xiàn)了 WAI-ARIA APG 的 tree widget 模式tree.md Accessibility 一節(jié)包含鍵盤導(dǎo)航、正確的 roles 與 ARIA 屬性。新無障礙特性要求使用levelAccessor/childrenAccessor使用舊式treeControl的樹因向后兼容原因無法實現(xiàn)正確的無障礙行為。兩個關(guān)鍵點isExpandable所有可展開的mat-tree-node/mat-nested-tree-node必須設(shè)置該屬性樹才能正確判斷節(jié)點可展開性(activation)事件樹節(jié)點通過鍵盤激活時會觸發(fā)activation輸出可用于執(zhí)行與點擊等價的操作mat-tree-node *matTreeNodeDeflet node (click)performAction(node) (activation)performAction($event) /mat-tree-node這里$event攜帶節(jié)點數(shù)據(jù)與matTreeNodeDef隱式導(dǎo)出的數(shù)據(jù)一致。對應(yīng)地MatTreeNode與MatNestedTreeNode的指令聲明中都聲明了outputs: [activation, expandedChange]node.tsAPI 報告里也能看到這兩個事件。六、MatTreeModule模塊聲明與使用方式MatTreeModuletree-module.ts的?mod聲明揭示了完整的依賴圖importsCdkTreeModule 7 個 Material 指令MatNestedTreeNode、MatTreeNodeDef、MatTreeNodePadding、MatTreeNodeToggle、MatTree、MatTreeNode、MatTreeNodeOutletexportsBidiModule提供 RTL 方向支持 同樣的 7 個指令。因此在實際項目中使用樹功能只需在模塊中導(dǎo)入MatTreeModule即可無需額外導(dǎo)入 CDK Tree 模塊或 Bidi 模塊import {MatTreeModule} from angular/material/tree; NgModule({ imports: [MatTreeModule], }) export class MyModule {}由于MatTree、MatTreeNode等均通過providers: [{provide: CdkXxx, useExisting: MatXxx}]覆蓋了 CDK 的注入令牌即便同時導(dǎo)入了CdkTreeModule也不會產(chǎn)生重復(fù)邏輯。七、進(jìn)一步探索從 API 報告到源碼與測試API 報告是了解angular/material_tree公開面的最快入口但要真正掌握行為細(xì)節(jié)建議繼續(xù)閱讀倉庫內(nèi)以下資源使用文檔src/material/tree/tree.md——包含扁平/嵌套樹模板、toggle、padding、條件模板、數(shù)據(jù)源接入、無障礙等完整示例核心實現(xiàn)tree.ts、node.ts、padding.ts、toggle.ts、outlet.ts數(shù)據(jù)源flat-data-source.ts、nested-data-source.ts測試用例tree.spec.ts、tree-using-tree-control.spec.ts、tree-using-legacy-key-manager.spec.ts分別覆蓋新式訪問器、treeControl與舊式鍵盤管理器三條路徑測試 Harnesstesting/tree-harness.ts 與 testing/tree-harness.spec.ts可用于組件測試中定位樹節(jié)點、讀取展開狀態(tài)等樣式與主題tree.scss、_tree-theme.scss、_m2-tree.scss 與 _m3-tree.scssM2/M3 雙主題實現(xiàn)Golden 基準(zhǔn)goldens/material/tree/index.api.md 本身即公開 API 的權(quán)威基準(zhǔn)倉庫通過 API Extractor 校驗源碼與基準(zhǔn)的一致性任何 API 變更都會在此文件中體現(xiàn)。總結(jié)angular/material_tree的公開 API 高度凝練7 個指令/組件加上 2 個其中一個已棄用的數(shù)據(jù)源與 1 個扁平化工具類全部建立在 CDK Tree 之上。通過 API 報告可以清晰分辨出當(dāng)前推薦使用的MatTreeMatTreeNodelevelAccessor/childrenAccessor/isExpandable新式組合與已標(biāo)記棄用的MatTreeFlatDataSource/MatTreeFlattener/tabIndex/disabled計劃在 21.0.0 移除。理解這份 API 報告就掌握了這一組件的全部能力邊界與演進(jìn)方向——這正是一份由 API Extractor 生成的 API 報告相較于普通使用文檔的獨(dú)特價值所在。【免費(fèi)下載鏈接】componentsComponent infrastructure and Material Design components for Angular項目地址: https://gitcode.com/GitHub_Trending/co/components創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考