在 Angular
中有三種
類型的指令directive
:
組件@Component
— 擁有模板的指令,我們?cè)诹硗庖晃囊褜iT學(xué)習(xí)過啦虽惭。本文就不涉及改指令的內(nèi)容了耘眨。
結(jié)構(gòu)型指令 — 通過添加和移除 DOM 元素改變 DOM 布局的指令浮还。例如,*NgFor
和 *NgIf
。你可能注意到了指令名的星號(hào)*
前綴滨攻,它是一個(gè)語法糖
, 從內(nèi)部實(shí)現(xiàn)來說蓝翰,Angular
把 *...
屬性 翻譯成一個(gè) <ng-template>
元素 并用它來包裹宿主元素光绕,代碼如下:
<div *ngIf="hero" class="name">{{hero.name}}</div>
<ng-template [ngIf]="hero">
<div class="name">{{hero.name}}</div>
</ng-template>
<!-- ------------------------------ -->
<div *ngFor="let hero of heroes; let i=index;
let odd=odd; trackBy: trackById"
[class.odd]="odd">
({{i}}) {{hero.name}}
</div>
<ng-template ngFor
let-hero [ngForOf]="heroes" let-i="index"
let-odd="odd" [ngForTrackBy]="trackById">
<div [class.odd]="odd">
({{i}}) {{hero.name}}
</div>
</ng-template>
屬性型指令 — 改變?cè)亍⒔M件或其它指令的外觀和行為的指令畜份。例如诞帐,內(nèi)置的 NgStyle
指令可以同時(shí)修改元素的多個(gè)樣式
A. 屬性型指令
屬性型指令至少需要一個(gè)帶有
@Directive
裝飾器的控制器類。該裝飾器指定了一個(gè)用于標(biāo)識(shí)屬性的選擇器爆雹。 控制器類實(shí)現(xiàn)了指令需要的指令行為停蕉。本節(jié)展示了如何創(chuàng)建一個(gè)簡單的屬性型指令appHighlight
愕鼓,當(dāng)用戶把鼠標(biāo)懸停在一個(gè)元素上時(shí),改變它的背景色慧起。你可以這樣用它:
<!-- 使用屬性型指令 -->
<p appHighlight>Highlight me!</p>
指令名為啥不直接叫做
highlight
菇晃?
① 盡管highlight
是一個(gè)比appHighlight
更簡潔的名字,而且它確實(shí)也能工作蚓挤。 但是最佳實(shí)踐是在選擇器名字前面添加前綴磺送,以確保它們不會(huì)與標(biāo)準(zhǔn)HTML
屬性沖突。 它同時(shí)減少了與第三方指令
名字發(fā)生沖突的危險(xiǎn)屈尼。
② 確認(rèn)你沒有
為highlight
指令添加ng
前綴册着。 那個(gè)前綴屬于 Angular,使用它可能導(dǎo)致難以診斷的bug
import { Directive, ElementRef } from '@angular/core';
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
constructor(el: ElementRef) {
el.nativeElement.style.backgroundColor = 'yellow';
}
}
ElementRef
:注入宿主DOM
元素的引用脾歧,也就是你放置appHighlight
的那個(gè)元素甲捏。它通過其nativeElement
屬性給你了直接訪問宿主DOM
元素的能力。
當(dāng)前鞭执,appHighlight
只是簡單的設(shè)置元素的顏色司顿。 這個(gè)指令應(yīng)該在用戶鼠標(biāo)懸浮一個(gè)元素時(shí),設(shè)置它的顏色兄纺。
把 HostListener
(這個(gè)我們?cè)?code>@Component一文中也略有提到)加進(jìn)導(dǎo)入列表中
import { Directive, ElementRef, HostListener } from '@angular/core';
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
constructor(private el: ElementRef) { }
@HostListener('mouseenter') onMouseEnter() {
this.highlight('yellow');
}
@HostListener('mouseleave') onMouseLeave() {
this.highlight(null);
}
private highlight(color: string) {
this.el.nativeElement.style.backgroundColor = color;
}
}
運(yùn)行本應(yīng)用并確認(rèn):當(dāng)把鼠標(biāo)移到 p
上的時(shí)候大溜,背景色就出現(xiàn)了,而移開時(shí)就消失了
使用 @Input
數(shù)據(jù)綁定向指令傳遞值
import { Directive, ElementRef, HostListener, Input } from '@angular/core';
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
constructor(private el: ElementRef) { }
@Input('appHighlight') highlightColor: string;
@HostListener('mouseenter') onMouseEnter() {
this.highlight(this.highlightColor || 'red');
}
@HostListener('mouseleave') onMouseLeave() {
this.highlight(null);
}
private highlight(color: string) {
this.el.nativeElement.style.backgroundColor = color;
}
}
測(cè)試案例:
<h1>My First Attribute Directive</h1>
<h4>Pick a highlight color</h4>
<div>
<input type="radio" name="colors" (click)="color='lightgreen'">Green
<input type="radio" name="colors" (click)="color='yellow'">Yellow
<input type="radio" name="colors" (click)="color='cyan'">Cyan
</div>
<p [appHighlight]="color">Highlight me!</p>
B. 結(jié)構(gòu)型指令
有時(shí)你會(huì)希望只有當(dāng)特定的條件為真時(shí)才重復(fù)渲染一個(gè)
HTML
塊估脆。 但Angular
不允許钦奋。這是因?yàn)槟阍谝粋€(gè)元素上只能放一個(gè)結(jié)構(gòu)型
指令。
結(jié)構(gòu)型指令可能會(huì)對(duì)宿主元素及其子元素做很復(fù)雜的事疙赠。當(dāng)兩個(gè)指令放在同一個(gè)元素上時(shí)付材,誰先誰后?
NgIf
優(yōu)先還是NgFor
優(yōu)先圃阳?NgIf
可以取消NgFor
的效果嗎厌衔? 如果要這樣做,Angular
應(yīng)該如何把這種能力泛化捍岳,以取消其它結(jié)構(gòu)型指令的效果呢富寿?
在文章開頭我們已經(jīng)介紹了*ngIf
和*ngFor
再來具體解析下*ngFor
-
let
關(guān)鍵字聲明一個(gè)模板輸入變量,你會(huì)在模板中引用它锣夹。本例子中页徐,這個(gè)輸入變量就是hero
、i
和odd
银萍。 解析器會(huì)把let hero
泞坦、let i
和let odd
翻譯成命名變量let-hero
、let-i
和let-odd
砖顷。 - 微語法解析器接收
of
和trackby
贰锁,把它們首字母大寫(of
->Of
,trackBy
->TrackBy
)赃梧, 并且給它們加上指令的屬性名(ngFor
)前綴,最終生成的名字是ngForOf
和ngForTrackBy
豌熄。 這兩個(gè)最終生成的名字是NgFor
的輸入屬性授嘀,指令據(jù)此了解到列表是heroes
,而 track-by 函數(shù)是trackById
锣险。 -
API 參考手冊(cè)中描述了
NgFor
指令的其它屬性和上下文屬性蹄皱。
NgSwitch
了解下
Angular
的 NgSwitch
實(shí)際上是一組相互合作的指令:NgSwitch
、NgSwitchCase
和 NgSwitchDefault
芯肤。
<div [ngSwitch]="hero?.emotion">
<app-happy-hero *ngSwitchCase="'happy'" [hero]="hero">
</app-happy-hero>
<app-sad-hero *ngSwitchCase="'sad'" [hero]="hero">
</app-sad-hero>
<app-confused-hero *ngSwitchCase="'confused'" [hero]="hero">
</app-confused-hero>
<app-unknown-hero *ngSwitchDefault [hero]="hero">
</app-unknown-hero>
</div>
<div [ngSwitch]="hero?.emotion">
<ng-template [ngSwitchCase]="'happy'">
<app-happy-hero [hero]="hero"></app-happy-hero>
</ng-template>
<ng-template [ngSwitchCase]="'sad'">
<app-sad-hero [hero]="hero"></app-sad-hero>
</ng-template>
<ng-template [ngSwitchCase]="'confused'">
<app-confused-hero [hero]="hero"></app-confused-hero>
</ng-template >
<ng-template ngSwitchDefault>
<app-unknown-hero [hero]="hero"></app-unknown-hero>
</ng-template>
</div>
C. ng-template
& ng-container
<ng-template>
元素:是一個(gè) Angular 元素
巷折,用來渲染 HTML
。 它永遠(yuǎn)不會(huì)直接顯示出來崖咨。 事實(shí)上锻拘,在渲染視圖之前,Angular
會(huì)把 <ng-template>
及其內(nèi)容替換為一個(gè)注釋击蹲。
如果沒有使用結(jié)構(gòu)型指令署拟,而僅僅把一些別的元素包裝進(jìn) <ng-template>
中,那些元素就是不可見的歌豺。結(jié)構(gòu)型指令會(huì)讓 <ng-template>
正常工作
<ng-container>
的救贖:類似React
的Fragments
還有一個(gè)問題是:有些 HTML
元素需要所有的直屬下級(jí)都具有特定的類型推穷。 比如,<select>
元素要求直屬下級(jí)必須為 <option>
类咧,那就沒辦法把這些選項(xiàng)包裝進(jìn) <div>
或 <span>
中馒铃。如:
<div>
Pick your favorite hero
(<label><input type="checkbox" checked
(change)="showSad = !showSad">show sad</label>)
</div>
<select [(ngModel)]="hero">
<span *ngFor="let h of heroes">
<span *ngIf="showSad || h.emotion !== 'sad'">
<option [ngValue]="h">{{h.name}} ({{h.emotion}})</option>
</span>
</span>
</select>
上述代碼的效果:下拉列表就是空的
Angular
的 <ng-container>
是一個(gè)分組元素,但它不會(huì)污染樣式或元素布局痕惋,因?yàn)?Angular
壓根不會(huì)把它放進(jìn) DOM
中骗露。<ng-container>
是一個(gè)由 Angular
解析器負(fù)責(zé)識(shí)別處理的語法元素。 它不是一個(gè)指令血巍、組件、類或接口珊随,更像是 JavaScript
中 if
塊中的花括號(hào)
<div>
Pick your favorite hero
(<label><input type="checkbox" checked
(change)="showSad = !showSad">show sad</label>)
</div>
<select [(ngModel)]="hero">
<ng-container *ngFor="let h of heroes">
<ng-container *ngIf="showSad || h.emotion !== 'sad'">
<option [ngValue]="h">{{h.name}} ({{h.emotion}})</option>
</ng-container>
</ng-container>
</select>
下拉框工作正常
自定義結(jié)構(gòu)型指令
在本節(jié)中述寡,你會(huì)寫一個(gè)名叫 UnlessDirective
的結(jié)構(gòu)型指令,它是 NgIf
的反義詞
import { Directive, Input, TemplateRef, ViewContainerRef }
from '@angular/core';
/**
* Add the template content to the DOM unless the condition is true.
*/
@Directive({ selector: '[appUnless]'})
export class UnlessDirective {
private hasView = false;
constructor(
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef) { }
//沒有人會(huì)讀取 appUnless 屬性叶洞,因此它不需要定義 getter
@Input() set appUnless(condition: boolean) {
if (!condition && !this.hasView) {
this.viewContainer.createEmbeddedView(this.templateRef);
this.hasView = true;
} else if (condition && this.hasView) {
this.viewContainer.clear();
this.hasView = false;
}
}
}
把這個(gè)指令添加到 AppModule
的 declarations
數(shù)組中鲫凶。然后創(chuàng)建一些 HTML
來試用一下。
<p *appUnless="condition" class="unless a">
(A) This paragraph is displayed because the condition is false.
</p>
<p *appUnless="!condition" class="unless b">
(B) Although the condition is true,
this paragraph is displayed because appUnless is set to false.
</p>
Angular
有哪些內(nèi)置指令?
等本人學(xué)習(xí)了解后再來補(bǔ)充吧...