? ? ? ?ng-template、ng-content声旺、ng-container三者應該是自定義組件需要經(jīng)常用到的指令笔链。今天咱們就來簡單了解下ng-template、ng-content腮猖、ng-container的基本用法鉴扫。
一、ng-content
? ? ? ?ng-content是內(nèi)容映射指令(也叫內(nèi)容嵌入)澈缺,內(nèi)容映射指的是在組件中嵌入模板代碼坪创,方便定制可復用的組件,很好地擴充組件的功能姐赡,方便代碼的復用莱预。
我們再往簡單一點想ng-content相當于一個占位符(留了個位置),類似于路由出口router-outlet一樣雏吭。之后會把相應的內(nèi)容放到這個位置上來锁施。
1.2 ng-content select 組件選擇
? ? ? ?ng-content提供select 屬性,方便我們選擇投影的內(nèi)容(組件或者html里面的標簽)杖们,如果我們沒有設置select屬性則所有的內(nèi)容都可以投影上來悉抵。select的選擇規(guī)則很簡單,就三種規(guī)則:
- select="xx"選擇摘完,xx對應html里面標簽或者組件的名字姥饰。比如select="div"表示ng-content位置只會放div標簽。
<ng-content select="div"></ng-content>
- select=".xx"選擇孝治,xx對應html標簽或者組件的class名字列粪。比如select=".select-class"表示ng-content位置只會放設有class="select-class"的html標簽或者組件。
<ng-content select=".select-class"></ng-content>
- select="[key=value]"選擇谈飒,key-value的形式岂座。選擇設置了屬性key="value“的html標簽或者組件。比如select="[name=test]"表示ng-content位置只會放設置了屬性name=”test“的html標簽或者組件杭措。
select="[key]" 也是類型费什,ng-content會選擇設置有key的屬性的html標簽或者組件。
<ng-content select="[name=test]"></ng-content>
<div name="test">我是第一號位置 div[name="test"]</div>
強調(diào)一點select的值不能設置為動態(tài)的
1.2 ngProjectAs
? ? ? ?通過ng-content的select屬性可以指定html標簽或者組件投射ng-content位置上來手素。但是呢有個限制條件鸳址。不管是select標簽或者組件的名字、或者class泉懦、或者是屬性他們都是作用在直接子節(jié)點上稿黍。還是用一個簡單但額實例來說明
// 我們先自定義一個組件app-content-section 里面會使用ng-content
import {Component} from '@angular/core';
@Component({
selector: 'app-content-section',
template: `
<div>
<h1>ng content</h1>
<div style="background-color: #039be5">
<ng-content select="app-content-child"></ng-content>
</div>
</div>
`,
styleUrls: ['./content-section.component.less']
})
export class ContentSectionComponent {
}
// 下面中情況下 ng-content沒有投射到對應的內(nèi)容
<app-content-section>
<ng-container>
<app-content-child [title]="'測試下'"></app-content-child>
</ng-container>
</app-content-section>
// 通過使用 ngProjectAs 讓ng-content的內(nèi)容能正確的投射過來。
<app-content-section>
<ng-container ngProjectAs="app-content-child">
<app-content-child [title]="'測試下'"></app-content-child>
</ng-container>
</app-content-section>
1.3 ng-conent 包含組件的獲取
? ? ? ?ng-conent提供了@ContentChild和@ContentChildren來獲取ng-conent里面包含的組件(類似@ViewChild和@ViewChildren)崩哩。獲取到ng-conent里面的組件之后你就可以為所欲為了巡球。
? ? ? ?我覺得如果有需要獲取ng-content包含組件的情況,前提條件是咱得對放在ng-content位置的是啥類型的組件心里有數(shù)。有一點要注意@ContentChild和@ContentChildren所在的ts文件一定是有ng-content對應的哪個ts文件辕漂∧卦睿可千萬別搞錯了。
接下來我們用一個特別簡單的例子
// 定義一個app-content-section組件
import {AfterContentInit, Component, ContentChild, ContentChildren, QueryList} from '@angular/core';
import {ContentChildComponent} from '../child/content-child.component';
/**
* 想獲取ng-content里面的組件的使用@ContentChild或者@ContentChildren
*/
@Component({
selector: 'app-content-section',
template: `
<div>
<h1>ng content</h1>
<!--這里我們確定我們這里會放ContentChildComponent組件钉嘹,才好使用@ContentChild和@ContentChildren-->
<ng-content></ng-content>
</div>
`,
styleUrls: ['./content-section.component.less']
})
export class ContentSectionComponent implements AfterContentInit {
// 通過 #section_child_0 獲取組件
@ContentChild('section_child_0')
childOne: ContentChildComponent;
// 通過 ContentChildComponent 組件名獲取組件
@ContentChildren(ContentChildComponent)
childrenList: QueryList<ContentChildComponent>;
ngAfterContentInit(): void {
console.log(this.childOne);
this.childrenList.forEach((item) => {
console.log(item);
});
}
}
// 使用app-content-section
import {Component} from '@angular/core';
@Component({
selector: 'app-ng-content',
template: `
<app-content-section>
<app-content-child #section_child_0 [title]="title_0"></app-content-child>
<app-content-child #section_child_1 [title]="title_1"></app-content-child>
</app-content-section>
`,
styleUrls: ['./ng-content.component.less']
})
export class NgContentComponent {
title_0 = 'child_0';
title_1 = 'child_1';
}
二、ng-template
? ? ? ?ng-template是Angular 結(jié)構(gòu)型指令中的一種鲸阻,用于定義模板渲染HTML(模板加載)跋涣。定義的模板不會直接顯示出來,需要通過其他結(jié)構(gòu)型指令(如 ng-if)或 template-ref 將模塊內(nèi)容渲染到頁面中鸟悴。
? ? ? ?上面既然說了ng-template的內(nèi)容默認是不會在頁面上顯示出來的陈辱。這肯定不行呀,咱們使用ng-template不管中間的原因是啥细诸,反正最后肯定是要把這些內(nèi)容顯示在界面上的沛贪。那么咱們有哪些辦法來顯示ng-template的內(nèi)容呢。
- 借助其他結(jié)構(gòu)型指令如×ngIf震贵,來顯示ng-template的內(nèi)容利赋。
<!-- 通過ngIf結(jié)構(gòu)型指令顯示ng-template的內(nèi)容 -->
<div class="lessons-list" *ngIf="condition else elseTemplate">
判斷條件為真
</div>
<ng-template #elseTemplate>
<div>判斷條件為假</div>
</ng-template>
- 通過TemplateRef、ViewContainerRef把ng-template對應的元素顯示出來猩系。TemplateRef對應ng-template的引用媚送,ViewContainerRef呢則是view容器的引用用來操作DOM元素。
import {AfterViewInit, Component, TemplateRef, ViewChild, ViewContainerRef} from '@angular/core';
@Component({
selector: 'app-template-section',
template: `
<ng-template #tpl>
Hello, ng-template!
</ng-template>
`,
styleUrls: ['./template-section.component.less']
})
export class TemplateSectionComponent implements AfterViewInit {
@ViewChild('tpl')
tplRef: TemplateRef<any>;
constructor(private vcRef: ViewContainerRef) {
}
ngAfterViewInit() {
// 這樣tplRef對應的ng-template內(nèi)容就顯示出來了
this.vcRef.createEmbeddedView(this.tplRef);
}
}
- 通過NgTemplateOutlet指令來顯示已有的ng-template對應的視圖寇甸。NgTemplateOutlet指令用于基于已有的 TemplateRef 對象塘偎,插入對應的內(nèi)嵌視圖。同時我們可以通過 [ngTemplateOutletContext] 屬性來設置 ng-template 的上下文對象拿霉。綁定的上下文應該是一個對象吟秩,ng-template中通過 let 語法來聲明綁定上下文對象屬性名。
import {AfterViewInit, Component, TemplateRef, ViewChild, ViewContainerRef} from '@angular/core';
@Component({
selector: 'app-ng-template',
template: `
<!-- let-param 取的是綁定對象myContext的$implicit字段的指绽淘,let-param相和let-param="$implicit"是等價的涵防, -->
<!-- let-name="name" 取的是綁定對象myContext里面name字段對應的值-->
<ng-template #inputTemplateWithContent let-param let-name="name">
<div>{{param}} - {{name}}</div>
</ng-template>
<ng-container *ngTemplateOutlet="inputTemplateWithContent; context: myContext"></ng-container>
`,
styleUrls: ['./ng-template.component.less']
})
export class NgTemplateComponent {
myContext = {$implicit: '默認值', name: 'tuacy'};
}
這里想稍微提下通過NgTemplateOutlet綁定上下文對象的問題,在ng-template中我們通過let-xx="yy" xx是在ng-template內(nèi)部使用的變量名字收恢。xx對應的值是上下文對象yy屬性的值武学。而且let-xx等同于let-xx="
implicit: '默認值', name: 'tuacy'};
ng-template里面let-param param對應的是myContext對象里面$implicit屬性的值(let-param和let-param=”implicit“是一樣的意思)伦意,let-name="name" name對應的是myContext對象里面name屬性對應的值火窒。
? ? ? ?我們來一個稍微復雜一點的例子,我們把ng-template的內(nèi)容驮肉,做為一個組件的參數(shù)熏矿。
import {Component, Input, TemplateRef} from '@angular/core';
@Component({
selector: 'app-template-input',
template: `
<!-- 沒有傳遞參數(shù)的時候就使用defaultTemplate里面的布局 -->
<ng-template #defaultTemplate>
<div>咱們沒有傳遞參數(shù)</div>
</ng-template>
<ng-container *ngTemplateOutlet="inputTemplate ? inputTemplate: defaultTemplate"></ng-container>
`,
styleUrls: ['./template-input.component.less']
})
export class TemplateInputComponent {
/**
* 模板作為參數(shù)
*/
@Input()
inputTemplate: TemplateRef<any>;
}
// 使用的時候的代碼
<!-- 我們定義一個組件,把ng-template的內(nèi)容作為參數(shù)傳遞進去 -->
<ng-template #paramTemplate>
<div>我是參數(shù)</div>
</ng-template>
<app-template-input [inputTemplate]="paramTemplate"></app-template-input>
2.1 ngTemplateOutlet
? ? ? ?ngTemplateOutlet指令用于基于已有的 TemplateRef對象,插入對應的內(nèi)嵌視圖票编。同時在使用 ngTemplateOutlet 指令的時候褪储,我們可以通過 [ngTemplateOutletContext]屬性來設置 EmbeddedViewRef 的上下文對象。綁定的上下文應該是一個對象慧域,而且可以通過 let 語法來聲明綁定上下文對象屬性名鲤竹。
在渲染頁面之前,Angular 會把及其內(nèi)容替換為注釋
三昔榴、ng-container
? ? ? ?ng-container既不是一個Component辛藻,也不是一個Directive,只是單純的一個特殊tag互订。ng-container可以直接包裹任何元素吱肌,包括文本,但本身不會生成元素標簽仰禽,也不會影響頁面樣式和布局氮墨。包裹的內(nèi)容,如果不通過其他指令控制吐葵,會直接渲染到頁面中规揪。
咱們可以把ng-container簡單理解為一個邏輯容器。用來做一些邏輯處理的折联。
? ? ? ?咱們在講ng-template的時候就出現(xiàn)了ng-container粒褒。ng-container一個重要的作用就是和ng-template一起使用。ng-container還有一個用處就是配合ngFor和×ngIf使用诚镰。我們知道ngFor和×ngIf不能同時處在一個元素上奕坟。所以咱們想要在不添加額外的html標簽的情況下達到同樣的效果就得請出ng-container。具體參見如下的代碼清笨。
import {Component} from '@angular/core';
@Component({
selector: 'app-ng-container',
template: `
<h1>ng-container</h1>
<ul>
<ng-container *ngFor="let item of list;let index=index">
<li *ngIf="index%2 === 0">
{{"index is " + index + " value is " + item}}
</li>
</ng-container>
</ul>
`,
styleUrls: ['./ng-container.component.less']
})
export class NgContainerComponent {
list = ['1號位置', '2號位置', '3號位置', '4號位置', '5號位置', '6號位置', '7號位置', '8號位置', '9號位置'];
}
? ? ? ?關(guān)于ng-template月杉、ng-content、ng-container的內(nèi)容咱們就講這些抠艾。也是為了咱們解析來的自定義控件做一些準備苛萎。 本文中設計到的代碼下載地址 https://github.com/tuacy/ng-dynamic