今天當我在做 Angular 開發時,一個知識點引起了我的注意:
在檢查 DOM 時,我看到 ngcontent 被 Angular 應用于元素。 嗯……如果它們包含了最終 DOM 中的元素,那麼 有什麼用? 當時我對 和 的差別感到困惑。
在尋求知道我的問題的答案的過程中,我發現了 的概念。 令我驚訝的是,還有另一個容易混淆的概念: *ngTemplateOutlet。 我開始了我的旅程,尋求對兩個概念的澄清,但現在我有四個,聽起來幾乎一樣!
你遇到過這種情況嗎? 如果是,那麼您來對地方了。 是以,事不宜遲,讓我們一一介紹。
1.
顧名思義, 是一個模闆元素,Angular 與結構指令(*ngIf、*ngFor、[ngSwitch] 和自定義指令)一起使用。
這些模闆元素僅在存在結構指令時才起作用。 Angular 将宿主元素(指令所應用到的元素)包裝在 中,并通過用診斷注釋(diagnostic comments)替換它來使用完成的 DOM 中的 。
考慮一個簡單的 *ngIf 示例:
上面顯示的是 *ngIf 的 Angular 解釋,也就是解除文法糖之後的實際代碼。 Angular 将應用指令的宿主元素放在 中,并保持宿主原樣。 最終的 DOM 類似于我們在本文開頭看到的:
2.
我們很多人編寫這段代碼的原因是無法在 Angular 中的單個宿主元素上使用多個結構指令。 現在這段代碼工作正常,但如果 item.id 是一個可能不需要的虛假值,它會在 DOM 中引入幾個額外的空
。
人們可能不會關心像這樣的簡單示例,但是對于具有複雜 DOM(顯示數萬個資料)的大型應用程式,這可能會變得很麻煩,因為元素可能具有附加到它們的偵聽器,這些偵聽器仍然存在于 DOM 監聽事件。
更糟糕的是應用樣式 (CSS) 必須執行的嵌套級别!
不用擔心,我們有 來救援!
Angular 是一個不會幹擾樣式或布局的分組元素,因為 Angular 不會将它放在 DOM 中。
使用 ng-container 重寫。
可以了解成把 div 标簽放置到 ng-container 這個虛無的容器裡,當 div 的 *ngIf 指令布爾值為 false 時,虛無的容器連同裡面的 div 标簽壓根就不會生成。
最後渲染出的 HTML 代碼裡,沒有多餘的空 div 标簽了:
最佳實踐:當我們隻想應用多個結構指令而不在我們的 DOM 中引入任何額外元素時,我們應該使用 。
3.
它們用于建立可配置元件。 這意味着可以根據使用者的需要配置元件。 這就是衆所周知的内容投影: Content Projection. 已釋出庫中使用的元件使用 使自己可配置。
考慮一個簡單的 元件,下圖是其本身的 HTML 定義:
顯然,footer 區域允許動态配置内容。
下圖展示了如何為 footer 區域動态注入自定義 footer 内容。這種用法稱為單一投射。
元件的開始和結束标記中傳遞的 HTML 内容就是要投影的内容。 這就是我們所說的内容投影。 内容将在提供内容投影功能元件内的 内呈現。
這允許 元件的使用者在元件内傳遞任何自定義頁腳并準确控制他們希望如何呈現它。
Multiple Projections
如果您可以決定哪些内容應該放在什麼地方呢? 除了将每個内容投影到單個 中之外,您還可以使用 的 select 屬性控制内容的投影方式。 它需要一個元素選擇器來決定在特定的 中投射哪些内容。
就是這樣:
我們修改了 定義以執行多内容投影。 select 屬性選擇将在特定 中呈現的内容類型。 這裡我們首先選擇渲染标題 h1 元素。 如果投影内容沒有 h1 元素,它将不會呈現任何内容。 同樣,第二個選擇查找 div。 其餘内容在最後一個 中呈現,沒有選擇。
如何消費這個帶有 select 屬性的,允許多重投射的元件?方法如下所示:
4. *ngTemplateOutlet
*ngTemplateOutlet 用于兩種場景:
在視圖的各個部分插入一個通用模闆,而不考慮循環或條件
制作高度配置的元件。
模闆重用
考慮一個視圖,您必須在多個位置插入模闆。 例如,要放置在網站中的公司徽标。 我們可以通過為徽标編寫一次模闆并在視圖中的任何地方重用它來實作它。
以下是代碼片段:
如您所見,我們隻編寫了一次徽标模闆,并在同一頁面上使用一行代碼将其使用了 3 次!
Customizable components
*ngTemplateOutlet 的第二個用例是高度定制的元件。 考慮我們之前的 元件示例,并進行了一些修改:
以上是 元件的修改版本,它接受三個輸入屬性 —— headerTemplate、bodyTemplate、footerTemplate。 以下是 project-content.ts 的片段:
模闆檔案裡使用到的 input 屬性,headerTemplate,bodyTemplate 和 footerTemplate 屬性定義在 Component 檔案裡。
我們在這裡試圖實作的是顯示從 的父元件接收到的頁眉、正文和頁腳。 如果未提供其中任何一個,我們的元件将在其位置顯示預設模闆。 是以,建立了一個高度定制的元件。
要使用我們最近修改的元件:
這就是我們将模闆引用傳遞給我們的元件的方式。 如果其中任何一個未通過,則元件将呈現預設模闆。
ng-content vs. *ngTemplateOutlet
它們都可以幫助我們實作高度定制化的元件,但選擇哪個以及何時選擇?
可以清楚地看到,如果沒有提供, *ngTemplateOutlet 為我們提供了更多顯示預設模闆的能力。
這不是 ng-content 的情況。 它按原樣呈現内容。 您最多可以在 select 屬性的幫助下拆分内容并在視圖的不同位置呈現它們。 您不能有條件地呈現 ng-content 中的内容。 您必須顯示從父級收到的内容,而無法根據内容做出決定。
但是,在兩者中進行選擇完全取決于您的用例。 至少現在我們的武器庫中有一個新武器 *ngTemplateOutlet,除了 ng-content 的功能外,它還提供了對内容的更多控制!