Angular 基於自定義指令的內容投影 content projection 問題的單步除錯
問題描述
本文涉及到的程式碼位置:https://github.com/wangzixi-diablo/ngDynamic
我有一個能接受內容投影的 Angular Component:
具體投影內容,通過 ng-container
和指令 ngTemplateOutlet
指定。
ngTemplateOutlet
的來源是 content
,這個屬性是 Component 通過下列 content query 得到的結果:
@ContentChild(ZippyContentDirective) content!: ZippyContentDirective;
從語義上講,消費 app-example-zippy 的 Component 的 HTML 模板裡,只有有 ng-template
ZippyContentDirective
對應的 attribute selector,則該 ng-template
裡的內容會被自動投影到 app-example-zippy
內部。
然而執行時,我們觀察不到上圖高亮的第四行內容,而是在 console 裡看到如下錯誤訊息:
ERROR TypeError: Cannot read properties of undefined (reading 'templateRef')
at ZippyComponent_div_1_Template (template.html:3:19)
at executeTemplate (core.js:7511:9)
at refreshView (core.js:7380:13)
at refreshEmbeddedViews (core.js:8481:17)
at refreshView (core.js:7404:9)
at refreshComponent (core.js:8527:13)
at refreshChildComponents (core.js:7186:9)
at refreshView (core.js:7430:13)
at refreshComponent (core.js:8527:13)
at refreshChildComponents (core.js:7186:9)
問題分析
content 內容為 undefined,說明 content query 執行失敗了:
app-example-zippy 內部的 ng-template
,使用的 attribute Directive 同ZippyContentDirective
定義的 selector 不一致,如下圖所示:
糾正之後問題消失。
總結
Ivy 在進行渲染時,需要跟蹤三種資料:Template、Logical Tree 和 Render Tree。在我們的許多資料結構中,為了簡潔起見,這三個概念被縮寫為 T、L 和 R 字首。
模板是原始碼的解析版本。它包含以 Ivy 指令和有關元件/指令的元資料的形式呈現模板的指令。如果您可以在原始碼中找到它,那麼模板資料結構中的相應欄位也將出現。無論其中的程式碼是否已執行,模板資訊都存在。例如,即使條件為假,*ngIf
在 Ivy 中,模板資訊儲存在 TView(以及 TData 和 TNode)資料結構中。這些資料結構一起提供了關於模板 Ivy 在執行時需要的所有靜態資訊。 靜態
這個詞對於將其與另一個 Ivy 裡重要的 Logic View
概念相區分開。