Angular裝飾器介紹

       裝飾器的作用就是在添加裝飾器的地方在不改動原有代碼的情況下增加額外的功能。Angular框架中裝飾器是一個函數。他將元數據添加到類、類成員(屬性、方法)和函數參數上。讓它們在不需要做任何代碼變動的前提下增加額外功能。

1 類裝飾器

       類裝飾器負責把元數據附加到類上,以瞭解類的設計意圖以及這個類是如何工作的。Angular框架裏面類裝飾器有多種.如下所示:

裝飾器 解釋 備註
@NgModule 模塊裝飾器(幫把相關的一些代碼邏輯組織在一起) NgModule 可以將其組件和一組相關代碼(如服務)關聯起來,形成功能單元。每個Angular應用都有一個根模塊,通常命名爲AppModule
@Component 組件裝飾器 組件可以認爲是屏幕上的一個視圖. 組件定義視圖。每個Angular應用都至少有一個組件,也就是根組件
@Injectable 依賴裝飾器(把一個類定義爲服務) 組件使用服務。對於與特定視圖無關並希望跨組件共享的數據或邏輯,可以創建服務類。
@Pipe 管道裝飾器 管道的作用就是傳輸。並且不同的管道具有不同的作用。(其實就是處理數據)
@Directive 指令裝飾器 用來控制組件的某些行爲

1.1 @NgModule

       @NgModule用來描述Angular應用裏面的模塊(NgModule)的。也就是說NgModule是一個帶有@NgModule裝飾器的類。@NgModule的參數是一個元數據對象,用於描述如何編譯組件的模板,以及如何在運行時創建注入器。

和其他語言一樣,模塊指的就是把某些功能整合到一起形成一個模塊.模塊話編程.

       Angular應用是模塊化的,擁有自己的模塊化系統.一個模塊就是一個NgModule。每個NgModule就是一個容器,用於存放一些內聚的代碼塊,這些代碼塊專注於某個應用領域、或者某個工作流或一組緊密相關的功能。它可以包含一些組件、服務提供商或其它代碼文件,其作用域由包含它們的NgModule定義。它還可以導入一些由其它模塊中導出的功能,並導出一些指定的功能供其它NgModule使用。

       每個Angular應用至少有一個模塊,也就是根模塊,習慣上命名爲AppModule,位於一個名叫app.module.ts的文件中。引導這個根模塊就可以啓動你的應用。

1.1.1 @NgModule元數據解釋

選項 類型 說明
providers? Provider[] 列出當前模塊需要的一些共用的服務,這樣我們就可以在這個模塊的各個組件中通過依賴注入使用這些服務了
declarations? Array<Type 聲明屬於這個模塊的指令,管道等等。模塊內部Components/Directives/Pipes的列表
imports? Array<Type | ModuleWithProviders | any[]> 當前模塊需要依賴的一些其他的模塊,這樣做的目的就是使我們這個模塊,可以直接使用別的模塊exports提供的一些指令,組件等等
exports? Array<Type | any[]> 當前模塊需要導出的一些組件,指令,模塊等,這樣如果別的模塊導入了我們這個模塊,那麼別的模塊就可以直接使用我們在這裏導出的組件,指令模塊等
entryComponents? Array<Type | any[]> 一個組件的集合,它應該和當前組件一起編譯。對於這裏列出的每個組件,Angular 都會創建一個 ComponentFactory 並保存進 ComponentFactoryResolver 中(動態組件)
bootstrap? Array<Type | any[]> 應用的主視圖,稱爲根組件。它是應用中所有其它視圖的宿主。只有根模塊才應該設置這個 bootstrap 屬性
schemas? Array<SchemaMetadata | any[]> 不屬於Angular的組件或者指令的元素或者屬性都需要在這裏進行聲明,允許設置的值有: NO_ERRORS_SCHEMA 和 CUSTOM_ELEMENTS_SCHEMA。NO_ERRORS_SCHEMA: 當前模板中存在未知選擇器時它不會顯示錯誤;CUSTOM_ELEMENTS_SCHEMA: 當前模板中可以使用任何類型的自定義元素,這有助於在應用程序中合併除Angular組件之外的Web組件
id? string 它可以是一個名字或者一個路徑,用來在調用getModuleFactory的時候區別模塊,如果這個屬性是undefined那麼這個模塊將不會被註冊,如果有設置id,可以通過getModuleFactory()來獲取到當前模塊
jit? true 如果是true,則當前模塊使用JIT編譯,否則使用AOT編譯。JIT: 即Just-in-time,動態(即時)編譯,邊運行邊編譯。AOT: Ahead Of Time,指運行前編譯。

1.1.2 NgModule作用:

  • NgModule 最主要的作用是幫助開發者組織業務代碼,把關係比較緊密的一些功能(組件)代碼組合在一起。

  • NgModule 用來控制組件、指令、管道等是否可以使用,處於同一個 NgModule 裏面的組件默認互相可見,而對於外部的組件來說,只能看到 NgModule 導出(exports )的內容,也就是說,如果你定義的 NgModule不exports任何內容,那麼外部使用者即使 import了你這個模塊,也沒法使用裏面定義的任何內容。

  • NgModule 是打包時候用到的最小單位,打包的時候會檢查所有 @NgModule 和路由配置,Angular底層是使用webpack打包。因爲Angular已經幫我們配置好了webpack,所以開發者輕鬆很多,否則就需要自己配置環境。

1.2 @Directive

       @Directive修飾的類是指令類。在Angular應用中指令一般用來控制組件(DOM)的某些行爲。Angular框架也默認給我們提供很多指令,有結構型指令,屬性型指令。

  • 結構型指令: 通過添加和移除DOM元素改變DOM佈局的指令. 如NgFor和NgIf.
  • 屬性型指令: 改變元素、組件或其它指令的外觀和行爲的指令。 如NgStyle 和NgSwitch.

1.1.1 @Directive元數據解釋

選項 類型 說明
selector? string css選擇器名,用於在模板中標記出該指令(組件),並觸發其實例化
inputs? string[] 該指令(組件)的輸入參數,和@Input裝飾器的作用是相同的
outputs? string[] 該指令(組件)可供事件綁定的輸出屬性
host? {[key: string]: string;} 使用一組鍵-值對,把類的屬性映射到宿主元素的綁定(Property、Attribute 和事件)
providers? Provider[] 服務提供商的集合
exportAs? string 一個或多個名字,可以用來在模板中把該指令賦值給一個變量。當有多個名字時,請使用逗號分隔它們
queries? {[key:string]:any} 配置將要注入到該指令中的一些查詢。內容查詢會在調用 ngAfterContentInit 回調之前設置好。 試圖查詢會在調用 ngAfterViewInit 回調之前設置好。

inputs和outputs: 當前指令(組件)的輸入和輸出。但是最新的Angular版本已經不推薦使用這兩個屬性了,推薦使用@Input(),@Output()來代替。

@Component({
  selector: 'app-report-template',
  template:
    `
       <div>
         <pre>{{hero}}</pre>
         <button (click)="deleteRequestInit()">Get</button>
       </div>
     `,
  inputs: ['hero'],
  outputs: ['deleteRequest'],
})
export class ReportTemplateComponent {

  public deleteRequest = new EventEmitter();

  deleteRequestInit() {
    this.deleteRequest.emit({'message': 'Are you sure you want to delete this record!.'})
  }
}

// 等價於下面的代碼

@Component({
  selector: 'app-report-template',
  template:
    `
       <div>
         <pre>{{hero}}</pre>
         <button (click)="deleteRequestInit()">Get</button>
       </div>
     `,
})
export class ReportTemplateComponent {

  @Input()
  hero;

  @Output()
  deleteRequest = new EventEmitter<any>();

  deleteRequestInit() {
    this.deleteRequest.emit({'message': 'Are you sure you want to delete this record!.'})
  }
}

host:使用一組鍵-值對,把類的屬性映射到宿主元素的綁定(Property、Attribute和事件)。

      Angular在變更檢測期間會自動檢查宿主Property綁定。如果綁定的值發生了變化,Angular 就會更新該指令的宿主元素。當 key 是宿主元素的 Property 時,這個 Property 值就會傳播到指定的 DOM 屬性。當 key 是 DOM 中的靜態 Attribute 時,這個 Attribute 值就會傳播到宿主元素上指定的 Property 去。對於事件處理:它的 key 就是該指令想要監聽的 DOM 事件。 要想監聽全局事件,請把要監聽的目標添加到事件名的前面。 這個目標可以是 window、document 或 body。它的value就是當該事件發生時要執行的語句。如果該語句返回 false,那麼就會調用這個DOM事件的preventDefault函數。這個語句中可以引用局部變量 $event 來獲取事件數據。比如如下的代碼實例。

    @Component({
       host: {
         '[class.nav-static]' : 'config.state["nav-static"]',
         '[class.chat-sidebar-opened]' : 'chatOpened',
         '[class.app]' : 'true',
         id: 'app'
       }
     })
   
    @Component({
       host: {
         'class' : 'myclass1 myclass2 myclass3'
       }
     })
   
    @Component({
       host: {
         '(window:blur)': 'focusOutFunction($event)',
         '(window:focus)': 'focusInFunction($event)',
       }
     })

exportAs:一個或多個名字,可以用來在模板中把該指令賦值給一個變量。當有多個名字時,請使用逗號分隔它們,我們用一個簡單的例子來說明下:

   1. 定義一個指令 AdHostDirective,exportAs: 'adHost'
    @Directive({
       selector: '[ad-host]',
       exportAs: 'adHost'
    })
    export class AdHostDirective {
       constructor(public viewContainerRef: ViewContainerRef) {
       }
    }
   
   
    2. 在模板中使用該指令,我們同事添加了兩次 #test='adHost',#test1='adHost'
   
     <div>
       <!-- 動態組件存放的容器 -->
       <ng-template ad-host #test='adHost'></ng-template>
       <!-- 動態組件存放的容器 -->
       <ng-template ad-host #test1='adHost'></ng-template>
     </div>
   
  
    3. 最後我們需要在對應的ts裏面獲取到該指令對象

     // @ViewChild(AdHostDirective) adHost: AdHostDirective; 單個的情況,可以使用,多個的情況比較麻煩
     @ViewChild('test') adHost: AdHostDirective;
     @ViewChild('test1') adHost1: AdHostDirective;
   
   
    通過exportAs 如果同意個模板裏面有同一個指令多個的話,我們可以很簡單的獲取到對應的指令。如果不用這種方式很麻煩的

queries:配置將要注入到該指令(組件)中的一些查詢,內容查詢會在調用 ngAfterContentInit 回調之前設置好。 試圖查詢會在調用 ngAfterViewInit 回調之前設置好。說白了queries的使用就是@ViewChild,@ViewChildren,@ContentChil,@ContentChildren的使用。比如如下兩段代碼是等價的。

@Component({
  selector: 'app-report-template',
  templateUrl: './report-template.component.html',
  styleUrls: ['./report-template.component.less'],
  queries: {
    adHost: new ViewChild(AdHostDirective)
  }
})
export class ReportTemplateComponent {
  
  adHost: AdHostDirective;

}

// 等價於下面的代碼

@Component({
  selector: 'app-report-template',
  templateUrl: './report-template.component.html',
  styleUrls: ['./report-template.component.less'],
  viewProviders: [ReportTemplateService]
})
export class ReportTemplateComponent {
  
  @ViewChild(AdHostDirective) 
  adHost: AdHostDirective;

}

1.2 @Component

       聲明一個組件時,在組件類上要用@Component裝飾器來告知Angular這是一個組件。

       組件控制屏幕上被稱爲視圖的一小片區域。在組件類總定義組件的應用邏輯。在Angular應用中組件(Component)是屬於模塊(NgMoude)的。組件是比模塊更小的一個單元。

1.2.1 @Component元數據解釋

       因爲Component是繼承Directive的,Component也是指令的一種.所以指令能做到的事情Component也能做到.上面講的@Directive元數據都適用於@Component。

除了@Directive的元數據之外,@Component元數據元數據還有如下屬性。

選項 類型 說明
changeDetection? ChangeDetectionStrategy 當組件實例化之後,Angular 就會創建一個變更檢測器,它負責傳播組件各個綁定值的變化。 該策略是下列值之一:ChangeDetectionStrategy#OnPush(0) 把策略設置爲 CheckOnce(按需);ChangeDetectionStrategy#Default(1) 把策略設置爲 CheckAlways
viewProviders? Provider[] 定義一組可注入對象,它們在視圖的各個子節點中可用
moduleId? string 包含該組件的那個模塊的 ID。該組件必須能解析模板和樣式表中使用的相對 URL。 SystemJS 在每個模塊中都導出了 __moduleName 變量。在 CommonJS 中,它可以設置爲module.id
templateUrl? string 組件模板文件的 URL。如果提供了它,就不要再用 template 來提供內聯模板了
template? string 組件的內聯模板。如果提供了它,就不要再用 templateUrl 提供模板了
styleUrls? string[] 一個或多個 URL,指向包含本組件 CSS 樣式表的文件
styles? string[] 本組件用到的一個或多個內聯 CSS 樣式
animations? any[] 一個或多個動畫 trigger() 調用,包含一些 state() 和 transition() 定義
encapsulation? ViewEncapsulation 供模板和 CSS 樣式使用的樣式封裝策略
interpolation? [string, string] 改寫默認的插值表達式起止分界符({{ 和 }})
entryComponents? Array<Type | any[]> 一個組件的集合,它應該和當前組件一起編譯。對於這裏列出的每個組件,Angular 都會創建一個 ComponentFactory 並保存進 ComponentFactoryResolver 中
preserveWhitespaces? boolean 爲 true 則保留,爲 false 則從編譯後的模板中移除可能多餘的空白字符。 空白字符就是指那些能在 JavaScript 正則表達式中匹配 \s 的字符。默認爲 false,除非通過編譯器選項改寫了它

encapsulation:供模板和 CSS 樣式使用的樣式封裝策略。取值有如下幾種:ViewEncapsulation.Native(使用 Shadow DOM。它只在原生支持 Shadow DOM的平臺上才能工作)、ViewEncapsulation.Emulated(使用墊片(shimmed)CSS來模擬原生行爲)、ViewEncapsulation.None(使用全局CSS,不做任何封裝。如果沒有提供,該值就會從CompilerOptions中獲取它)。默認的編譯器選項是ViewEncapsulation.Emulated。如果該策略設置爲ViewEncapsulation.Emulated,並且該組件沒有指定styles或styleUrls,就會自動切換到ViewEncapsulation.None。

1.3 @Pipe

       @Pipe修飾的類是管道類。在Angular應用中,管道的作用就是傳輸,可以把一個值通過某種變換成一個新的值。(其實就是對數據做進一步的處理)

1.3.1 @Pipe元數據

選項 類型 說明
name string 管道對應的名字,在模板文件裏面綁定管道的時候使用
pure? boolean 如果爲true,則管道是pure的,這意味着只有在其輸入參數發生更改時纔會調用transform()方法。管道默認是pure的。如果管道具有內部狀態(即,結果取決於其參數以外的狀態),則將“pure”設置爲false。在這種情況下,即使參數未更改,也會在每個更改檢測週期調用管道。

1.3.2 Pipe使用

       我們自定義一個文件大小轉換的管道.把相應的文件大小轉換成對應的單位對應的大小.自定義一個FileSizePipe管道.代碼如下.

import {Pipe, PipeTransform} from '@angular/core';

/**
 * 文件大小轉換
 */
@Pipe({
  name: 'fileSize'
})
export class FileSizePipe implements PipeTransform {

  private static TB_UNIT = 'TB';
  private static GB_UNIT = 'GB';
  private static MB_UNIT = 'MB';
  private static KB_UNIT = 'KB';
  private static B_UNIT = 'B';

  // TB
  private static fileSizeTb(fileSize: number) {
    return fileSize / (1024 * 1024 * 1024 * 1024);
  }

  // GB
  private static fileSizeGb(fileSize: number) {
    return fileSize / (1024 * 1024 * 1024);
  }

  // MB
  private static fileSizeMb(fileSize: number) {
    return fileSize / (1024 * 1024);
  }

  // KB
  private static fileSizeKb(fileSize: number) {
    return fileSize / 1024;
  }

  /**
   * {{ value | fileSize }}
   * {{ value | fileSize:fractionDigits }}
   * {{ value | fileSize:fractionDigits:extension }}
   */
  transform(value: number, fractionDigits?: number, extension?: 'B' | 'KB' | 'MB' | 'GB' | 'TB'): string {
    if (value == null) {
      return null;
    }
    const realFractionDigits = fractionDigits == null ? 2 : fractionDigits;
    if (extension == null) {
      // TB
      if (value >= (1024 * 1024 * 1024 * 1024)) {
        return FileSizePipe.fileSizeTb(value).toFixed(realFractionDigits) + FileSizePipe.TB_UNIT;
      } else if (value >= (1024 * 1024 * 1024)) {
        return FileSizePipe.fileSizeGb(value).toFixed(realFractionDigits) + FileSizePipe.GB_UNIT;
      } else if (value >= (1024 * 1024)) {
        return FileSizePipe.fileSizeMb(value).toFixed(realFractionDigits) + FileSizePipe.MB_UNIT;
      } else if (value >= 1024) {
        return FileSizePipe.fileSizeKb(value).toFixed(realFractionDigits) + FileSizePipe.KB_UNIT;
      } else {
        return value.toFixed(realFractionDigits) + FileSizePipe.B_UNIT;
      }
    } else {
      switch (extension) {
        case "TB":
          return FileSizePipe.fileSizeTb(value).toFixed(realFractionDigits) + FileSizePipe.TB_UNIT;
        case "GB":
          return FileSizePipe.fileSizeGb(value).toFixed(realFractionDigits) + FileSizePipe.GB_UNIT;
        case "MB":
          return FileSizePipe.fileSizeMb(value).toFixed(realFractionDigits) + FileSizePipe.MB_UNIT;
        case "KB":
          return FileSizePipe.fileSizeKb(value).toFixed(realFractionDigits) + FileSizePipe.KB_UNIT;
        case "B":
          return value.toFixed(realFractionDigits) + FileSizePipe.B_UNIT;
      }
    }
    return null;
  }

}


使用

<p>
  {{1024 | fileSize}}
</p>

<p>
  {{1024 * 10 | fileSize}}
</p>

<!-- 保留三位小數 -->
<p>
  {{1024 * 1024 * 3 | fileSize:3}}
</p>

<!-- 保留三位小數,並且轉換成MB -->
<p>
  {{1024 * 1024 * 2 | fileSize:3:'MB'}}
</p>

1.4 @Injectable

       @Injectable修飾的類,表示當前類是一個服務類。該類需要通過注入器根據服務提供者去創建服務對象。然後給需要使用的地方使用。這裏就涉及到Angular應用裏面的依賴注入問題了。有興趣的可以參考。

選項 類型 說明
providedIn Type | ‘root’ | null 指明當前服務注入的地方

當然了根據服務提供者的不同,還有其他不同的參數。具體可以參考咱們的上一篇文章   Angular依賴注入

2 屬性裝飾器

       屬性裝飾器:把裝飾器添加在屬性上,是屬性具有額外的一些功能。Angular系統裏面屬性裝飾器有很多,如下:

裝飾器 解釋 備註
@Input 屬性綁定(父組件向子組件傳遞數據)
@Output 事件綁定(子組件想父組件傳遞數據的同時觸發事件)
@HostBinding 爲宿主元素添加屬性值
@HostListener 爲宿主元素添加事件
@ContentChild 用於選擇當前組件引用的內容(從ng-content中獲取元素) 在父組件的 ngAfterContentInit 生命週期鉤子中才能成功獲取
@ContentChildren 同上(不過是儘可能多的匹配,有多少匹配多少) 在父組件的 ngAfterContentInit 生命週期鉤子中才能成功獲取
@ViewChild 從模板視圖中獲取匹配的元素(匹配到滿足條件的第一個) 在父組件的 ngAfterViewInit 生命週期鉤子中才能成功獲取
@ViewChildren 同上(不過@ViewChildren是儘可能多的匹配,有多少匹配多少) 在父組件的 ngAfterViewInit 生命週期鉤子中才能成功獲取

@Input

       @Input: 負責把父組件的數據傳遞到子組件。然後在子組件裏面做相應的處理。這個就沒什麼講的了,使用起來也簡單.

       Input裝飾器支持一個可選的參數,用來指定組件綁定屬性的名稱。如果沒有指定,則默認使用@Input對應裝飾的屬性名。不推薦爲起別名,推薦直接使用默認的名字。

       使用@Input的時候我們也可以通過setter,以攔截父組件中值的變化,在子組件中採取相應的動作。

@Output

       子組件暴露一個EventEmitter屬性,當事件發生時,子組件利用該屬性emits(向上彈射)事件。父組件綁定到這個事件屬性,並在事件發生時作出迴應。子組件的EventEmitter屬性是一個輸出屬性,通常帶有@Output 裝飾器。

@ViewChild、@ViewChildren

        @ViewChild、@ViewChildren:從模板視圖中獲取匹配的元素.

import {Component, ElementRef, ViewChild, ViewChildren, ViewContainerRef} from '@angular/core';
import {ViewChildChildComponent} from './view-child-child.component';

@Component({
  selector: 'app-decorator-view-child',
  template: `
    <h3>@ViewChild,@ViewChildren(從模板視圖中獲取匹配的元素)</h3>
    <app-view-child-child #childA></app-view-child-child>
    <app-view-child-child #childB></app-view-child-child>
    <button (click)="clickMe()" >點我</button>

  `
})
export class DecoratorViewChildComponent {

  /**
   * @ViewChild 的使用
   */
    // 使用模板變量名
  @ViewChild('childA')
  child10;
  // 使用類型查詢
  @ViewChild(ViewChildChildComponent)
  child11;
  // 使用模板變量名及設置查詢條件
  @ViewChild('childB', {read: ElementRef})
  child20;
  @ViewChild('childB', {read: ViewContainerRef})
  child21;

  /**
   * @ViewChildren 的使用
   */
  @ViewChildren(ViewChildChildComponent)
  children;

  clickMe() {
    this.child10.callFunction('child10');
    this.child11.name = '我是child2';

    this.child20.nativeElement.lastElementChild.firstElementChild.value = '我是child3~';
    this.child21._data.componentView.component.callFunction('child21');

    this.children._results[0].callFunction('children');
  }

}

@ContentChild、@ContentChildren

       @ContentChild、@ContentChildren:用於選擇當前組件引用的內容(從ng-content中獲取元素) .一般在自定義組件的時候使用.

比如如下的代碼,我們自定義一個組件ContentParentComponent.在這個組件裏面通過@ContentChild獲取到ng-conent裏面的內容

import {AfterContentInit, Component, ContentChild} from '@angular/core';
import {ContentChildComponent} from './content-child.component';

@Component({
  selector: 'app-content-parent',
  template: `
    <p>Parent Component</p>
    <!-- ng-content 讓使用該組件的人可以自定義裏面的內容 -->
    <ng-content></ng-content>
  `
})
export class ContentParentComponent implements AfterContentInit {

  // 通過類型獲取 -- ngAfterContentInit (事先我們明確了ng-content裏面會放置ContentChildComponent組件)
  @ContentChild(ContentChildComponent)
  contentChild: ContentChildComponent;

  constructor() {
  }

  ngAfterContentInit() {
    // 對應contentChild做相應的操作處理
    this.contentChild.initValue = '@ContentChild';
  }

}

@Hostbinding

       @Hostbinding:爲宿主元素添加屬性值.

@HostListener

       @HostListener:爲宿主元素添加事件.

下面我們通過一個簡單的實例來說明@Hostbinding,@HostListener的使用.咱們自定義一個指令RainbowDirective.這樣所有使用該指令的地方就是宿主元素了.這樣可以對添加了該指令的元素做相應的改變.

import {Directive, HostBinding, HostListener} from '@angular/core';

/**
 * 主要是說明@HostBinding、@HostListener使用
 */
@Directive({
  selector: '[appRainbow]',
  exportAs: 'appRainbow'
})
export class RainbowDirective {

  possibleColors = [
    'darksalmon', 'hotpink', 'lightskyblue', 'goldenrod', 'peachpuff',
    'mediumspringgreen', 'cornflowerblue', 'blanchedalmond', 'lightslategrey'
  ];

  // 爲宿主元素添加屬性值
  @HostBinding('style.color') color: string;
  @HostBinding('style.borderColor') borderColor: string;

  // 爲宿主元素添加事件
  @HostListener('keydown') onKeydown() {
    // 隨機去一個顏色
    const colorPick = Math.floor(Math.random() * this.possibleColors.length);
    this.color = this.borderColor = this.possibleColors[colorPick];
  }

}
<input appRainbow>

3 參數裝飾器

       將裝飾器添加在參數上面,一般都是構造函數的參數上。獲取注入器裏面提供的服務對象。在咱們Angular框架裏面注入器一般分爲兩種:組件注入器、模塊注入器。組件注入器裏面注入的服務對象一般通過@Component裝飾器裏面的provider元數據提供。模塊注入器裏面注入的服務對象一般通過@NgModule裝飾器裏面的provider元數據提供。

參數裝飾器 解釋 備註
@Inject 獲取注入器裏面注入的token對應的服務實例對象
@Optional 和@Inject類似,唯一的區別就是如果沒有找到依賴關係,注入器將提供null
@Self 獲取當前組件注入器裏面提供的服務實例對象 只能是當前組件注入器提供的對象,模塊注入器裏面的都不行
@SkipSelf 從祖先組件注入器或者模塊注入器裏面獲取提供的對象
@Host 獲取宿主元素注入器裏面注入的對象

@Inject

       @Inject 用於獲取注入器裏面注入的token對應的服務實例對象。@Inject也是咱們用的最多的一個獲取依賴對象的裝飾器。

@Optional

       @Optional和@Inject類似,唯一的區別就是當注入器裏面沒有找到token對應的對象的時候返回null。所以在使用@Optional的時候一定要做null值的處理。

@Self

       從當前組件注入器裏面查找依賴對象。再強調一遍是當前組件注入器裏面查找,找到了就找到了,沒找到就沒找到。

@Self查找範圍:當前組件注入器。

import {Component, Inject, Injector, OnInit, Self} from '@angular/core';
import {SelfComponentService} from './self-component.service';
import {TOKEN_SKIP_CLASS_PROVIDER} from "../parameter-decorator-constant";
import {SelfTokenComponentService} from "./self-token-component.service";

@Component({
  selector: 'app-self-decorator',
  template: `
    <h3>@Self -- 獲取當前組件(或者指令)注入器裏面注入的對象(NgModule裏面注入的都不行)</h3>
  `,
  providers: [
    SelfComponentService,
    {provide: TOKEN_SKIP_CLASS_PROVIDER, useClass: SelfTokenComponentService}
  ]
})
export class SelfDecoratorComponent implements OnInit {

  /**
   * @Self()只能獲取當前組件注入器中注入的服務,NgModule 注入器裏面注入的都不行
   */
  constructor(private injector: Injector,
              @Self() private componentService: SelfComponentService,
              @Self() @Inject(TOKEN_SKIP_CLASS_PROVIDER) private tokenComponentService: SelfTokenComponentService) {

    // // injector.get(SelfModuleService, null, InjectFlags.Self)這種寫法好像有點問題,講道理是獲取不到服務的
    // const service: SelfModuleService = injector.get(SelfModuleService, null, InjectFlags.Self);

  }

  ngOnInit() {
  }

}

       @Self也可以和@Optional一起使用.這樣在沒有找到的情況下賦null值.如下所示

@Optional() @Self() private componentService: SelfComponentService

@SkipSelf

       從當前元素對應注入器的祖先注入器中查找。

@SkipSelf查找範圍:祖先組件注入器以及模塊注入器。(排除自身組件注入器)

       @SkipSelf也可以和@Optional一起使用

@Host

       獲取宿主元素注入器裏面注入的對象。

父子組件關係不屬於宿主關係。所以@Host在父子關係使用不了。

       這裏爲了給大家講明白宿主關係,我們列出@Host的兩種場景。

  • 在指令裏面使用@Host(指令宿主關係)

       指令裏面使用@Host獲取宿主元素注入器裏面提供的對象。

import {Directive, Host, Inject} from '@angular/core';
import {HostComponentService} from './host-component.service';
import {TOKEN_HOST_CLASS_PROVIDER} from '../parameter-decorator-constant';
import {HostTokenComponentService} from './host-token-component.service';

@Directive({
  selector: '[appHostDecorator]'
})
export class HostDecoratorDirective {

  /**
   * @Host() 獲取宿主元素裏面提供的服務(宿主元素注入器提供的服務)
   * @param componentService
   * @param tokenService
   */
  constructor(@Host() private componentService: HostComponentService,
              @Host() @Inject(TOKEN_HOST_CLASS_PROVIDER) private tokenService: HostTokenComponentService) {
  }

}
  • 在ng-content使用@Host(ng-conent宿主關係)

自定義一個組件,注意使用了

import {Component} from '@angular/core';
import {HostComponentService} from './host-component.service';
import {TOKEN_HOST_CLASS_PROVIDER} from '../parameter-decorator-constant';
import {HostTokenComponentService} from './host-token-component.service';

@Component({
  selector: 'app-host-decorator',
  template: `
    <h3>@Inject -- 獲取注入器裏面指定token對應的服務實例對象</h3>
    <ng-content></ng-content>
  `,
  providers: [
    HostComponentService,
    {provide: TOKEN_HOST_CLASS_PROVIDER, useClass: HostTokenComponentService}
  ]
})
export class HostDecoratorComponent {

  constructor() {
  }

}

自定義一個子組件,我們會把該組件放在對應的內容裏面

import {Component, Host, Inject} from '@angular/core';
import {HostComponentService} from './host-component.service';
import {TOKEN_HOST_CLASS_PROVIDER} from '../parameter-decorator-constant';
import {HostTokenComponentService} from './host-token-component.service';

@Component({
  selector: 'app-host-decorator-child',
  template: `
    <p>ng-content對應的內容</p>
  `
})
export class HostDecoratorChildComponent {

  constructor(@Host() private componentService: HostComponentService,
              @Host() @Inject(TOKEN_HOST_CLASS_PROVIDER) private tokenService: HostTokenComponentService) {
  }

}
<app-host-decorator>
  <app-host-decorator-child></app-host-decorator-child>
</app-host-decorator>

       @Host也可以和@Optional一起使用.


       關於Angular裝飾器咱們就扯這麼一些.主要是也爲了讓大家在使用這些裝飾器的時候心裏有個底.不同的場景用不同的裝飾器.如果大家在使用過程中有什麼疑問,可以留言.能力範圍內儘量會解答的. 最好給出文章中的一些驗證代碼地址 https://github.com/tuacy/angular-decorator

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章