1. 程式人生 > 其它 >關於 ng-template 通過 @input 傳入另一個 Component 不能工作的問題除錯

關於 ng-template 通過 @input 傳入另一個 Component 不能工作的問題除錯

問題描述

本文設計到的程式碼倉庫:https://github.com/wangzixi-diablo/ngDynamic

host Component:

<ng-template #paramTemplate>
    <div>我是#paramTemplate裡的 div 標籤</div>
</ng-template>

<app-template-input inputTemplate='paramTemplate'></app-template-input>

我期望把 id 為 paramTemplate 的模板例項傳入子 Component:app-template-input

.

然而執行時 Chrome console 報錯:

這些錯誤沒有一個是在我書寫的程式碼之內:

ERROR TypeError: templateRef.createEmbeddedView is not a function
at ViewContainerRef.createEmbeddedView (core.js:10190:45)
at NgTemplateOutlet.ngOnChanges (ng_template_outlet.ts:65:28)
at NgTemplateOutlet.rememberChangeHistoryAndInvokeOnChangesHook (core.js:2373:14)
at callHook (core.js:3285:14)
at callHooks (core.js:3251:17)
at executeInitAndCheckHooks (core.js:3203:9)
at refreshView (core.js:7395:21)
at refreshComponent (core.js:8527:13)
at refreshChildComponents (core.js:7186:9)
at refreshView (core.js:7430:13)

問題分析

從呼叫棧 ViewContainerRef.createEmbeddedView 開始分析。設定斷點開始除錯:

重新重新整理頁面,斷點觸發:

檢查變數 templateRef,發現它的值是一個字串。

但是接下來的語句,需要呼叫 templateRefcreateEmbeddedView 方法。顯然,string 型別的變數,其原型鏈上是不可能有這個方法的,所以報錯。

我們注意到,當前的呼叫棧,發生在 ngTemplateOutletngOnChanges 方法內。

檢視 app-template-input 的實現,發現 @Input 接收的資料型別為 TemplateRef

,因為只有這個型別,才定義了 createEmbeddedView 方法。

我們的實現裡,錯誤地給其傳入了一個字串。

解決辦法

給屬性 inputTemplate 加上一對中括號,使用表示式繫結語法,這樣 paramTemplate 會被當作一個表示式執行,導致第 48 行的 paramTemplate 例項被傳給 @Input:

最後問題解決:

總結

這裡我們用 ng-container 裡包裹了一個結構指令 *ngTemplateOutlet.

看 app-template-input 的實現。

任何消費 TemplateInputComponent 的 parent Component,都需要給第 18 行的 @Input 賦一個模板例項。

賦值語法如下:

<app-template-input [inputTemplate]="paramTemplate"></app-template-input>

看起來給 inputTemplate 賦值的內容 paramTemplate 是一個字串,實際是一個表示式

最後生成的原始碼:

這裡使用 *ngTemplateOutlet,動態生成一個 template 例項。

也就是說,ng-template 用 # 定義的 id,和 ng-container, *ngTemplateOutlet 這三者是配合起來使用的。