Angular - 內容投影



內容投影是 Angular 元件中的一種技術,它允許將外部內容(來自元件的使用者)與佈局和樣式一起包含在元件模板的特定區域中。一個簡單的例子如下:

讓我們考慮一個元件,content-projection-sample 具有以下模板:

<p>This is content from the component template</p>
   <ng-content></ng-content>
<p>This is another content from component template</p> 

可以使用以下方法來包含外部資料:

<content-projection-sample>
   <p>This is content external content</p>
</content-projection-sample>

元件的輸出將如下所示:

<p>This is content from the component template</p>
<p>This is content external content</p>
<p>This is another content from component template</p> 

在這裡,外部內容放置/投影在 ng-content 的位置。

讓我們透過建立上面解釋的元件來理解內容投影。

步驟 1:建立一個新的元件,content-projection-sample

$ ng generate component content-projection-sample
CREATE src/app/content-projection-sample/content-projection-sample.component.css (0 bytes)
CREATE src/app/content-projection-sample/content-projection-sample.component.html (40 bytes)
CREATE src/app/content-projection-sample/content-projection-sample.component.spec.ts (680 bytes)
CREATE src/app/content-projection-sample/content-projection-sample.component.ts (276 bytes)
UPDATE src/app/app.module.ts (701 bytes)

步驟 2:在模板 content-projection-sample.component.html 中新增 ng-content 標籤,如下所示:

<p>This is content from the component template</p>
   <ng-content></ng-content>
<p>This is another content from component template</p> 

步驟 3:在 app 元件的 app.component.html 中使用 content-projection-sample 元件,如下所示:

<app-content-projection-sample>
   <p>This is external content</p>
</app-content-projection-sample>
<router-outlet></router-outlet>

步驟 4:執行應用程式並檢查輸出。

<app-content-projection-sample _ngcontent-ng-c2678441144="" _nghost-ng-c981906799="">
   <p _ngcontent-ng-c981906799=""> This is content from the component template</p>
   <p _ngcontent-ng-c2678441144=""> This is external content</p>
   <p _ngcontent-ng-c981906799=""> This is another content from component template</p>
</app-content-projection-sample>

步驟 5:應用程式的輸出如下所示:

Application Output

多插槽內容投影

只包含一個內容投影的元件稱為單插槽內容投影。Angular 也允許將多個內容投影到元件中,這稱為多插槽內容投影。讓我們看看如何透過修改上面的示例來使用多個內容投影。

  • 在元件模板 content-projection-sample.component.html 中新增另一個帶有選擇器屬性的 ng-content,如下所示:

<p>This is content from the component template</p>
<ng-content></ng-content>
<p>This is another content from component template</p>
<ng-content select="[second]"></ng-content>
  • 更新 app 元件模板 app.component.html,如下所示:

<app-content-projection-sample>
   <p>This is external content</p>
   <p second>This is yet another external content</p>
</app-content-projection-sample>

<router-outlet></router-outlet>

這裡,元件模板中設定的選擇器屬性(第二個)的值用於要投影的內容。

  • 現在,執行應用程式並檢查輸出。

<app-content-projection-sample _ngcontent-ng-c2332747330="" _nghost-ng-c2742507360="">
   <p _ngcontent-ng-c2742507360=""> This is content from the component template</p>
   <p _ngcontent-ng-c2332747330=""> This is external content</p>
   <p _ngcontent-ng-c2742507360=""> This is another content from component template</p>
   <p _ngcontent-ng-c2332747330="" second=""> This is yet another external content</p>
</app-content-projection-sample>
  • 應用程式的輸出如下所示:

multi-slot content

ngProjectAs

ngProjectAs 是一個特殊的屬性,用於在複雜場景中投影內容。一個例子是使用 ng-container 來佈局模板。眾所周知,ng-container 本身不渲染,而是渲染其子內容,我們需要使用 ngProjectAs 屬性來投影其內容。

讓我們更改上面的示例以使用 ng-container 和 ngProjectAs 屬性。

  • 更新 app 元件模板,**app.component.html**,如下所示:

<app-content-projection-sample>
   <p>This is external content</p>
   <ng-container ngProjectAs="second">
      <p>This is yet another external content</p>
   </ng-container>
</app-content-projection-sample>

<router-outlet></router-outlet>

這裡,元件模板中設定的選擇器屬性(第二個)的值用於 ng-container。

  • 現在,執行應用程式並檢查輸出。

<app-content-projection-sample _ngcontent-ng-c2332747330="" _nghost-ng-c2742507360="">
   <p _ngcontent-ng-c2742507360=""> This is content from the component template</p>
   <p _ngcontent-ng-c2332747330=""> This is external content</p>
   <p _ngcontent-ng-c2742507360=""> This is another content from component template</p>
   <p _ngcontent-ng-c2332747330="" second=""> This is yet another external content</p>
</app-content-projection-sample>
  • 應用程式的輸出如下所示:

ngProjectAs

條件內容投影

條件內容投影是在滿足特定條件時投影內容。我們可以使用 ng-content 來有條件地投影內容。但是不推薦這樣做,因為即使內容不會被渲染,ng-content 也會被初始化。相反,我們可以使用 ng-template 來安全地投影內容,因為它只會在內容將被渲染時才初始化內容。

使用 ng-template 投影內容的總體思路如下:

步驟 1:建立一個指令(例如 TestDirective)來定位和選擇 ng-template,它將用於儲存/包含要投影的內容。該指令將在建構函式中初始化期間獲取 ng-template 的引用。此模板引用將由父元件用來獲取內容並在適當的位置渲染它。

constructor(public tmpl_ref: TemplateRef<unknown>) { }

步驟 2:使用 @ContentChild 裝飾器在父元件(提供內容投影的元件)中獲取指令例項,如下所示:

@ContentChild(TestDirective) testDirectiveInstance!: TestDirective;

步驟 3:在父元件的模板中使用 ngTemplateOutlet、ngIf 和 ng-container 投影內容,如下所示:

<div *ngIf="condition">
   <ng-container [ngTemplateOutlet]="testDirectiveInstance.tmpl_ref"></ng-container>
</div>

這裡:

  • condition 是用於透過 *ngIf 切換內容投影的實際條件。

  • ngTemplateOutlet 是用於注入和渲染給定模板的內建指令。

  • testDirectiveInstance.tmpl_ref 是對實際模板的引用,該模板包含要投影的操作內容。該模板將使用 ng-template 放置在使用者元件的模板中。我們將在下一步中看到。

  • ng-container 用於確保在內容不會被投影時不會生成不需要的內容/標籤。

步驟 4:最後,透過在具有步驟 1 中建立的指令的模板中設定要投影的內容,在任何想要的地方使用該元件。

<ng-template appTestDirective>
   Hi, I am coming from conditional template
</ng-template>

讓我們透過更新我們的內容投影示例並更詳細地瞭解條件投影來建立一個可工作的演示。

步驟 1:使用 angular CLI 建立一個指令 greet-content,如下所示:

ng generate directive greet-content
CREATE src/app/greet-content.directive.spec.ts (249 bytes)
CREATE src/app/greet-content.directive.ts (153 bytes)
UPDATE src/app/app.module.ts (795 bytes)

步驟 2:更新指令並獲取模板引用,如下所示:

import { Directive, TemplateRef } from '@angular/core';

@Directive({
   selector: '[appGreetContent]'
})
export class GreetContentDirective {
   constructor(public template: TemplateRef<unknown>) { }
}

這裡:

  • selector 是識別指令的關鍵。

  • template 是透過建構函式注入(依賴注入概念)注入到指令中的 TemplateRef 型別引用物件。

步驟 3:更新元件 ContentProjectionSampleComponent 以獲取指令物件並設定實際條件,如下所示:

import { Component, ContentChild } from '@angular/core';
import { GreetContentDirective } from '../greet-content.directive';

@Component({
   selector: 'app-content-projection-sample',
   templateUrl: './content-projection-sample.component.html',
   styleUrls: ['./content-projection-sample.component.css']
})
export class ContentProjectionSampleComponent {
   show = true;
   @ContentChild(GreetContentDirective) greet!: GreetContentDirective;
}

這裡:

  • show 是一個變數,它儲存決定性條件。

  • @ContentChild 是一個裝飾器,它將用於獲取指令例項。

步驟 4:在元件的模板中,使用 ngIf 檢查條件,使用 ng-container 和 ngTemplateOutlet 在元件的模板中顯示模板 (greet.template),如下所示:

<p>This is content from the component template</p>
<ng-content></ng-content>
<p>This is another content from component template</p>
<ng-content select="[second]"></ng-content>

<div *ngIf="show">
   <ng-container [ngTemplateOutlet]="greet.template"></ng-container>
</div>

步驟 5:最後,使用該元件及其在 app 元件中的條件投影,如下所示:

<app-content-projection-sample>
   <p>This is external content</p>
   <ng-container ngProjectAs="second">
      <p>This is yet another external content</p>
   </ng-container>
   
   <ng-template appGreetContent>
      Hi, I am coming from conditional template
   </ng-template>
</app-content-projection-sample>

<router-outlet></router-outlet>

步驟 6:執行應用程式並檢查輸出,以發現內容是透過條件投影概念渲染的。

conditional projection

步驟 7:將元件中的條件 show 更新為 false 並檢查輸出,以發現 ng-template 內容未渲染。

export class ContentProjectionSampleComponent {
   show = false;
   @ContentChild(GreetContentDirective) greet!: GreetContentDirective;
}
Component Template
廣告