ng-template篷朵、ng-content、ng-container

? ? ? ?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"。比如上面的代碼我們綁定的對象是myContext = {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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市检号,隨后出現(xiàn)的幾起案子腌歉,更是在濱河造成了極大的恐慌,老刑警劉巖齐苛,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件翘盖,死亡現(xiàn)場離奇詭異,居然都是意外死亡凹蜂,警方通過查閱死者的電腦和手機馍驯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進店門阁危,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人汰瘫,你說我怎么就攤上這事狂打。” “怎么了混弥?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵趴乡,是天一觀的道長。 經(jīng)常有香客問我剑逃,道長浙宜,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任蛹磺,我火速辦了婚禮,結(jié)果婚禮上同仆,老公的妹妹穿的比我還像新娘萤捆。我一直安慰自己,他們只是感情好俗批,可當我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布俗或。 她就那樣靜靜地躺著,像睡著了一般岁忘。 火紅的嫁衣襯著肌膚如雪辛慰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天干像,我揣著相機與錄音帅腌,去河邊找鬼。 笑死麻汰,一個胖子當著我的面吹牛速客,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播五鲫,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼溺职,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了位喂?” 一聲冷哼從身側(cè)響起浪耘,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎塑崖,沒想到半個月后七冲,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡弃舒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年癞埠,在試婚紗的時候發(fā)現(xiàn)自己被綠了状原。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡苗踪,死狀恐怖颠区,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情通铲,我是刑警寧澤毕莱,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站颅夺,受9級特大地震影響朋截,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜吧黄,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一部服、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧拗慨,春花似錦廓八、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至烦却,卻和暖如春宠叼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背其爵。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工冒冬, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人醋闭。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓窄驹,卻偏偏與公主長得像,于是被迫代替她去往敵國和親证逻。 傳聞我的和親對象是個殘疾皇子乐埠,可洞房花燭夜當晚...
    茶點故事閱讀 43,627評論 2 350

推薦閱讀更多精彩內(nèi)容