Angular6 學習筆記——指令


angular6.x系列的學習筆記記錄,仍在不斷完善中,學習地址:

https://www.angular.cn/guide/template-syntax

http://www.ngfans.net/topic/12/post/2

 

系列目錄

(1)組件詳解之模板語法

(2)組件詳解之組件通訊

(3)內容投影, ViewChild和ContentChild

(4)指令

(5)路由

 

指令

 大家應該都知道,在html中存在一些附加在元素節點上的標記,例如屬性,事件等等.它們能夠改變元素的行為,甚至操作DOM,改變DOM元素,以及它的各級子節點.

 那么,在angular中也有這樣的存在,那就是指令.

 在 Angular 中有三種類型的指令:

  1. 組件 — 組件的特殊存在,擁有模板

  2. 結構型指令 — 通過添加和移除 DOM 元素改變 DOM 布局的指令,常用的有:*ngIf,*ngFor,ngSwitch

  3. 屬性型指令 — 改變元素、組件或其它指令的外觀和行為的指令,常用的有: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自帶的有許多內置的指令,下面可以看見許多熟悉的身影:

 

詳情參見官網傳送門,大家有興趣就多去看看

 

(終)

 

文檔信息

 


感謝您的閱讀,如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕。本文歡迎各位轉載,但是轉載文章之后必須在文章頁面中給出作者和原文連接


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM