动态创建 @ViewChild 导致运行时错误的原因分析
本文討論問題的代碼,位于 Github:https://github.com/wangzixi-diablo/ngDynamic
問題描述
我的 Component 代碼如下圖所示:
使用依賴注入,引入 ViewContainerRef,從而可以使用其 createEmbeddedView 方法,在運行時動態創建實例。
使用 @ViewChild,獲得該 Component HTML 源代碼里定義的 id 為 tpl 的模板實例,類型為 TemplateRef。
Component 的 HTML 源代碼:
然而啟動應用,出現運行時錯誤:
ERROR TypeError: Cannot read properties of undefined (reading ‘createEmbeddedView’)
at ViewContainerRef.createEmbeddedView (core.js:10190:45)
at NgTemplateComponent.push.8YnP.NgTemplateComponent.ngAfterViewInit (ng-template.component.ts:20:20)
at callHook (core.js:3281:18)
at callHooks (core.js:3251:17)
at executeInitAndCheckHooks (core.js:3203:9)
at refreshView (core.js:7451:21)
at renderComponentOrTemplate (core.js:7494:9)
at tickRootContext (core.js:8701:9)
at detectChangesInRootView (core.js:8726:5)
at RootViewRef.detectChanges (core.js:9991:9)
問題分析
上述調用上下文里,有一個棧幀是我們應用程序的代碼:
ngAfterViewInit (ng-template.component.ts:20:20)
在其方法內設置斷點, 發現運行時,this.tplRef 為空。
在值為 undefined 的變量上調用 createEmbeddedView 導致的這個錯誤。
問題轉化為:this.tplRef 的賦值邏輯是怎樣的?
在 ngOnInit 時,這個屬性還是 undefined 狀態:
我們把鼠標 hover 在 @ViewChild 上查看其說明:
變更檢測器會在視圖的 DOM 中查找能匹配上該選擇器的第一個元素或指令。 如果視圖的 DOM 發生了變化,出現了匹配該選擇器的新的子節點,該屬性就會被更新。
發現輸入參數是一個選擇器,本例我傳入的選擇器是 id 選擇器:tpl
根據 Angular 官網文檔,這意味著我的 HTML 模板文件里,tpl 之前應該用 # 修飾:
解決方案
在 tpl 前添加 #:
總結
如果我們想進一步觀察 view query 是如何根據傳入的選擇器 tpl,去 dom tree 里查找的節點,可以添加如下代碼,即 @ViewChild 和 set 函數搭配使用的情況。
@ViewChild('tpl')set thisNamedoesnotMatter(v:TemplateRef<any>){console.log('Jerry');this.tplRef = v;}通過調試,發現 view query 的執行過程不會顯示在 Chrome 開發者工具里,而僅僅顯示一個 dummy 的 XXX(Component 名稱)_Query 的調用上下文。
set 函數里的輸入參數 v 代表的就是 id 為 tpl 的 Template 實例。
總結
以上是生活随笔為你收集整理的动态创建 @ViewChild 导致运行时错误的原因分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: IBM 存储推出闪存产品新能力,帮助企业
- 下一篇: ng-template 使用过程中参数传