angular6.x系列的學習筆記記錄,仍在不斷完善中,學習地址:
系列目錄
(1)組件詳解之模板語法
(2)組件詳解之組件通訊
(3)內容投影, ViewChild和ContentChild
(4)指令
(5)路由
指令
大家應該都知道,在html中存在一些附加在元素節點上的標記,例如屬性,事件等等.它們能夠改變元素的行為,甚至操作DOM,改變DOM元素,以及它的各級子節點.
那么,在angular中也有這樣的存在,那就是指令.
在 Angular 中有三種類型的指令:
-
組件 — 組件的特殊存在,擁有模板
-
結構型指令 — 通過添加和移除 DOM 元素改變 DOM 布局的指令,常用的有:*ngIf,*ngFor,ngSwitch
-
屬性型指令 — 改變元素、組件或其它指令的外觀和行為的指令,常用的有:NgClass,NgStyle
組件
在查看源代碼,我們會發現這樣的畫面
所以說組件是繼承指令的,只是它比較特殊,有模版
同樣,因為組件繼承指令,在下面屬性型和結構型指令的一系列操作,在組件中都是可以實現的
但是因為指令的目的是為了復用,在組件中這樣操作是達不到這個目的,所以強烈不推薦這樣去操作.
這里就不多做說明,有興趣可以自己嘗試一下
屬性型指令
上面說道屬性型指令是用來改變元素、組件或其它指令的外觀和行為
那么我們如何打造屬於自己的屬性型指令呢?
首先,創建指令很像創建組件。
-
導入 Directive 裝飾器(而不再是 Component)。
-
導入符號 Input、TemplateRef 和 ViewContainerRef,視指令類型與需求來選擇
-
給指令類添加裝飾器。
-
設置 CSS 屬性選擇器 ,以便在模板中標識出這個指令該應用於哪個元素。
對於指令而言,至少需要一個帶有@Directive裝飾器的控制器類。該裝飾器指定了一個用於標識屬性的選擇器。 控制器類實現了指令需要的指令行為。
在我們存放指令的文件夾下(例如src/app/directives)下執行下面cli命令
ng generate directive name(簡寫ng g d name)
就可以快速得到src/app/directives/name.directive.ts和相應的測試文件src/app/directives/name.directive.spec.ts
並且在根模塊AppModule中聲明這個指令類。
根據命令快速創建一個名為highlight的指令。基本代碼如下:
1 import { BrowserModule } from '@angular/platform-browser'; 2 import { NgModule } from '@angular/core'; 3 4 import { AppRoutingModule } from './app-routing.module'; 5 import { AppComponent } from './app.component'; 6 import { HighlightDirective } from './directive/highlight.directive'; 7 8 @NgModule({ 9 declarations: [ 10 AppComponent, 11 HighlightDirective, 12 ], 13 imports: [ 14 BrowserModule, 15 AppRoutingModule 16 ], 17 providers: [], 18 bootstrap: [AppComponent] 19 }) 20 export class AppModule { }
1 import { Directive } from '@angular/core'; 2 3 @Directive({ 4 selector: '[appHighlight]' 5 }) 6 export class HighlightDirective { 7 8 constructor() { } 9 10 }
Warnning
和組件一樣,這些指令也必須在Angular模塊中進行聲明
指令所在的元素就是它的宿主元素
下面以一個實例展示如何自定義屬性型指令:highlight的功能如下
1能夠接收2個參數,其中一個為另外一個的默認值
2監聽宿主元素的鼠標進入和離開事件,在進入時宿主背景顏色為上述傳入的值
具體代碼如下
src/app/directives/highlight.directive.ts
1 import { Directive, ElementRef, HostListener, Input } from '@angular/core'; 2 3 @Directive({ 4 //指定指令的屬性型選擇器 5 selector: '[appHighlight]' 6 }) 7 8 export class HighlightDirective { 9 @Input('appHighlight') highlightColor: string; 10 @Input() defaultColor: string; 11 12 //構造函數中使用 ElementRef 來注入宿主 DOM 元素的引用 13 constructor(private el: ElementRef) { } 14 15 //監聽宿主元素mousenter事件 16 @HostListener('mouseenter') onMouseEnter() { 17 this.highlight(this.highlightColor || this.defaultColor); 18 } 19 20 //監聽宿主元素mousenter事件 21 @HostListener('mouseleave') onMouseLeave() { 22 this.highlight(null); 23 } 24 25 private highlight(color: string) { 26 //ElementRef通過其 nativeElement 屬性,提供直接訪問宿主 DOM 元素的能力。 27 this.el.nativeElement.style.backgroundColor = color; 28 } 29 }
宿主元素用法
1 <p appHighlight="red" defaultColor="black">宿主元素</p>
最終效果
結構性指令
結構性基本知識和上面的屬性型指令差不多,但是它必須導入 Input、TemplateRef 和 ViewContainerRef,因為我們必須得有值來確定的是什么樣的結構.
在說道結構性指令的時候,例如*ngIf,*ngFor(ngSwitch其實也有,不過是在內部),都會看到前面有一個*號,它是用來干嘛的呢?
其實這里星號是一個用來簡化更復雜語法的“語法糖”,從內部實現來說:以*ngIf為例,Angular 把 *ngIf 屬性翻譯成一個 <ng-template> 元素 並用它來包裹宿主元素
1 <ng-template [ngIf]="bool"> 2 <div class="name">{{hero.name}}</div> 3 </ng-template>
<ng-template>指令
<ng-template>是一個 Angular 元素,用來渲染 HTML。 它永遠不會直接顯示出來。 事實上,在渲染視圖之前,Angular 會把 <ng-template> 及其內容替換為一個注釋。如果沒有使用結構型指令,而僅僅把一些別的元素包裝進 <ng-template> 中,那些元素就是不可見的。
在下面的這個短語"Hip! Hip! Hooray!"中,中間的這個 "Hip!"不會顯示就是如此。
1 <p>Hip!</p> 2 <ng-template> 3 <p>Hip!</p> 4 </ng-template> 5 <p>Hooray!</p>
但是結構型指令會讓 <ng-template> 正常工作,在下面自定義結構型指令時就會看到這一點.
<ng-container> 指令
Angular 的 <ng-container> 是一個分組元素,但它不會污染樣式或元素布局,因為 Angular 壓根不會把它放進 DOM 中。
下面是重新實現的條件化段落,這次使用 <ng-container>,都可以顯示
1 <p> 2 I turned the corner 3 <ng-container *ngIf="bool"> 4 and saw {{hero.name}}. I waved 5 </ng-container> 6 and continued on my way. 7 </p>
TemplateRef 和 ViewContainerRef
像上面簡單結構型指令例子,會從 Angular 生成的 <ng-template> 元素中創建一個內嵌的視圖,並把這個視圖插入到一個視圖容器中.可以使用TemplateRef取得 <ng-template> 的內容,並通過ViewContainerRef來訪問這個視圖容器。可以把它們都注入到指令的構造函數中,作為該類的私有屬性,詳見下面自定義結構型指令
自定義結構型屬性
我們來自定義一個名為appUnless的結構型指令,該指令的使用者會把一個 true/false 條件綁定到 [appUnless] 屬性上。實現與*ngIf相反的功能
具體代碼如下
1 import { Directive, TemplateRef, Input, ViewContainerRef } from '@angular/core'; 2 3 @Directive({ 4 selector: '[appUnless]' 5 }) 6 export class UnlessDirective { 7 8 private hasView: boolean = false; 9 10 @Input() set appUnless(condition: boolean) { 11 if (!condition && !this.hasView) { 12 this.viewContainer.createEmbeddedView(this.templateref); 13 this.hasView = true; 14 } 15 else if (condition && this.hasView) { 16 this.viewContainer.clear(); 17 this.hasView = false; 18 } 19 } 20 21 constructor( 22 private templateref: TemplateRef<any>, 23 private viewContainer: ViewContainerRef 24 ) { } 25 26 }
<!-- ts里定義 condition: boolean = false;--> <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>
效果如下
對於簡單的段落,隱藏和移除之間的差異影響不大,但對於資源占用較多的組件是不一樣的。 當隱藏掉一個元素時,組件的行為還在繼續 —— 它仍然附加在它所屬的 DOM 元素上, 它也仍在監聽事件。Angular 會繼續檢查哪些能影響數據綁定的變更。 組件原本要做的那些事情仍在繼續。所在在使用結構型指令操作時,我們需要明白這一點
Warnning
每個宿主元素上只能有一個結構型指令,你可能試過把 *ngFor 和 *ngIf 放在同一個宿主元素上,但 Angular 不允許。這是因為你在一個元素上只能放一個結構型指令。
內置指令
angular自帶的有許多內置的指令,下面可以看見許多熟悉的身影:
詳情參見官網傳送門,大家有興趣就多去看看
(終)
文檔信息
- 發表作者: 半路獨行
- 發表出處: 博客園
- 原文地址: https://www.cnblogs.com/banluduxing/p/10402322.html
- 版權信息:
本作品采用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。
感謝您的閱讀,如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕。本文歡迎各位轉載,但是轉載文章之后必須在文章頁面中給出作者和原文連接。