在開發alice項目中,使用了阿里的ng-zorro框架,因此會常常遇到不少框架本身寫的屬性,例以下面的柵格:css
<div nz-row> <div nz-col [nzSpan]="4"> xxx </div> </div>
其中的nz-row,nz-col,以及nzSpan就是框架自定義的html屬性,但他們到底是什麼,又是怎麼實現的,我卻歷來沒有想過,因此,趁着本週學習時間比較充裕,就去探索了一下。
首先,在webstorm經過Ctrl
+ 鼠標點擊nz-col
查看它的源碼,如圖:html
發現nz-col實際上是一個指令,就去angular上搜索了指令的內容web
在 Angular 中有三種類型的指令:json
<yunzhi-data-list></yunzhi-data-list>
nz-row,nz-col
就是這類指令*ngFor,*ngIf
這三種指令中最經常使用的就是組件,因此我重點學習了後兩種指令。segmentfault
目標:使用指令實現給html元素綁定背景色:app
<button appHighlight = "yellow">高亮</button>
生成指令類文件,同生成組件同樣,使用CLI命令便可框架
ng generate directive highlight
而後就會多了這兩個文件:less
在指令的邏輯部分:webstorm
export class HighlightDirective implements OnInit { // 使用@input綁定傳給指令的值 @Input('appHighlight') highlightColor: string; constructor(private el: ElementRef) { // ElementRef就是該指令綁定的宿主元素 } ngOnInit(): void { // 設置該元素的css樣式的背景色爲輸入值 this.el.nativeElement.style.backgroundColor = this.highlightColor; } }
使用該指令:ide
<button appHighlight="yellow">高亮</button>
綁定多個屬性:
export class HighlightDirective implements OnInit { @Input('appHighlight') highlightColor: string; @Input() defaultColor: string; // 默認顏色 constructor(private el: ElementRef) { } ngOnInit(): void { // 若是highlightColor有值,則綁定highlightColor,沒有則綁定defaultColor this.el.nativeElement.style.backgroundColor = this.highlightColor || this.defaultColor; } }
使用:
<button appHighlight="yellow" defaultColor="red">高亮</button>
<button appHighlight defaultColor="red">高亮</button>
若是我寫的指令不想被人亂用,好比上述指令我只想在button中生效,就要用到指令的選擇器(selector
)了。
通常咱們命令生成指令文件後會有以下默認的代碼段:
@Directive({ selector: '[appHighlight]' })
這個selector裏的字符串就是咱們使用指令綁定到宿主元素的規則,或者說實例化指令對象的規則。
angular文檔提供的規則表以下:
例如,默認的綁定方法就是把該指令當成屬性,如:<button appHighlight>高亮</button>
若是我想只綁定給button,就能夠這樣寫:
@Directive({ selector: '[appHighlight] button' })
這樣一來,當我綁定到其餘元素時,編譯器就會報錯:
若是我不想使用默認的指令前綴app
,而是換成其餘的,如yunzhi
,則如圖修改tslint.json
文件
修改後:
{ "extends": "../tslint.json", "rules": { "directive-selector": [ true, "attribute", "yunzhi", "kebab-case" ], "component-selector": [ true, "element", "app", "kebab-case" ] } }
指令選擇器也修改前綴:
@Directive({ selector: '[yunzhi-highlight] button' })
使用指令:
<button yunzhi-highlight>高亮</button>
目標:編寫一個yunzhiUnless
指令,做用與ngIf相反,當變量值爲false,顯示宿主元素,爲true則移除不顯示。
<p *yunzhiUnless="condition">測試-p</p>
首先爲了讓指令更符合官方的規範,推薦在tsLint.json
指令選擇器使用格式修改成cameCase
,即默認設置
"directive-selector": [ true, "attribute", "yunzhi", "camelCase" ]
@Directive({ selector: '[yunzhiUnless]' })
新建指令unless
,注入TemplateRef,ViewContainerRef(沒懂下面這句話,先跟着往下寫就行)
使用TemplateRef取得 <ng-template> 的內容,並經過ViewContainerRef來訪問這個視圖容器
export class UnlessDirective { constructor(private viewContainer: ViewContainerRef, private templateRef: TemplateRef<any>) { } }
使用@Inout將一個布爾值綁定到yunzhiUnless
屬性上,當condition爲false,顯示,爲true,不顯示
@Input() set yunzhiUnless(condition: boolean) { if (!condition) { this.viewContainer.createEmbeddedView(this.templateRef); } else if (condition) { this.viewContainer.clear(); } }
在模板中使用該指令
<p *yunzhiUnless="condition">測試-p</p>
測試指令
html:
<p> condition={{condition | json}} </p> <label for="show">顯示</label> <input id="show" type="radio" [value]="false" [(ngModel)]="condition"> <label for="unshow">不顯示</label> <input id="unshow" type="radio" [value]="true" [(ngModel)]="condition"> <p *yunzhiUnless="condition">測試-p</p>
ts:
export class TestComponent { condition = false; }
效果:
如名字所示,TemplateRef 用於表示模板的引用
,但咱們注意到這裏並無使用ng-template
,爲何還能引用它呢?
這是由於*yunzhiUnless
實際上是一個語法糖
<p *yunzhiUnless="condition">測試-p</p>
等同於:
<ng-template [yunzhiUnless]="condition"> <p>測試-ng-template</p> </ng-template>
若是沒有使用結構型指令,而僅僅把一些別的元素包裝進 <ng-template> 中,那些元素就是不可見的。
ViewContainerRef
用於表示容器的引用,一般是span,div等
ViewContainerRef 對象提供了 createEmbeddedView() 方法,該方法接收 TemplateRef 對象做爲參數,並將模板中的內容做爲容器 (comment 元素) 的兄弟元素,插入到頁面中。
簡單理解就是以下圖,templateRef
指向ng-template
標籤,viewContainer
指向ng-container
標籤
簡單的瞭解了angular指令的實現後,發如今使用時減小了不少迷茫,有些之前不懂的細節也知道了爲何會這樣,不過也發現本身還有好多不懂的地方,好比模板和容器,DOM的一些操做等。