Angular裝飾器介紹

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

1 類裝飾器

? ? ? ?類裝飾器負責把元數(shù)據(jù)附加到類上菩佑,以了解類的設計意圖以及這個類是如何工作的。Angular框架里面類裝飾器有多種.如下所示:

裝飾器 解釋 備注
@NgModule 模塊裝飾器(幫把相關的一些代碼邏輯組織在一起) NgModule 可以將其組件和一組相關代碼(如服務)關聯(lián)起來凝化,形成功能單元稍坯。每個Angular應用都有一個根模塊,通常命名為AppModule
@Component 組件裝飾器 組件可以認為是屏幕上的一個視圖. 組件定義視圖搓劫。每個Angular應用都至少有一個組件瞧哟,也就是根組件
@Injectable 依賴裝飾器(把一個類定義為服務) 組件使用服務。對于與特定視圖無關并希望跨組件共享的數(shù)據(jù)或邏輯糟把,可以創(chuàng)建服務類绢涡。
@Pipe 管道裝飾器 管道的作用就是傳輸。并且不同的管道具有不同的作用遣疯。(其實就是處理數(shù)據(jù))
@Directive 指令裝飾器 用來控制組件的某些行為

1.1 @NgModule

? ? ? ?@NgModule用來描述Angular應用里面的模塊(NgModule)的雄可。也就是說NgModule是一個帶有@NgModule裝飾器的類。@NgModule的參數(shù)是一個元數(shù)據(jù)對象缠犀,用于描述如何編譯組件的模板数苫,以及如何在運行時創(chuàng)建注入器。

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

? ? ? ?Angular應用是模塊化的虐急,擁有自己的模塊化系統(tǒng).一個模塊就是一個NgModule。每個NgModule就是一個容器滔迈,用于存放一些內(nèi)聚的代碼塊止吁,這些代碼塊專注于某個應用領域、或者某個工作流或一組緊密相關的功能燎悍。它可以包含一些組件敬惦、服務提供商或其它代碼文件,其作用域由包含它們的NgModule定義谈山。它還可以導入一些由其它模塊中導出的功能俄删,并導出一些指定的功能供其它NgModule使用。

? ? ? ?每個Angular應用至少有一個模塊,也就是根模塊畴椰,習慣上命名為AppModule臊诊,位于一個名叫app.module.ts的文件中。引導這個根模塊就可以啟動你的應用斜脂。

1.1.1 @NgModule元數(shù)據(jù)解釋

選項 類型 說明
providers? Provider[] 列出當前模塊需要的一些共用的服務抓艳,這樣我們就可以在這個模塊的各個組件中通過依賴注入使用這些服務了
declarations? Array<Type<any> 聲明屬于這個模塊的指令,管道等等。模塊內(nèi)部Components/Directives/Pipes的列表
imports? Array<Type<any> | ModuleWithProviders | any[]> 當前模塊需要依賴的一些其他的模塊,這樣做的目的就是使我們這個模塊,可以直接使用別的模塊exports提供的一些指令,組件等等
exports? Array<Type<any> | any[]> 當前模塊需要導出的一些組件,指令,模塊等秽褒,這樣如果別的模塊導入了我們這個模塊,那么別的模塊就可以直接使用我們在這里導出的組件,指令模塊等
entryComponents? Array<Type<any> | any[]> 一個組件的集合壶硅,它應該和當前組件一起編譯。對于這里列出的每個組件销斟,Angular 都會創(chuàng)建一個 ComponentFactory 并保存進 ComponentFactoryResolver 中(動態(tài)組件)
bootstrap? Array<Type<any> | any[]> 應用的主視圖,稱為根組件椒舵。它是應用中所有其它視圖的宿主蚂踊。只有根模塊才應該設置這個 bootstrap 屬性
schemas? Array<SchemaMetadata | any[]> 不屬于Angular的組件或者指令的元素或者屬性都需要在這里進行聲明,允許設置的值有: NO_ERRORS_SCHEMA 和 CUSTOM_ELEMENTS_SCHEMA。NO_ERRORS_SCHEMA: 當前模板中存在未知選擇器時它不會顯示錯誤笔宿;CUSTOM_ELEMENTS_SCHEMA: 當前模板中可以使用任何類型的自定義元素犁钟,這有助于在應用程序中合并除Angular組件之外的Web組件
id? string 它可以是一個名字或者一個路徑,用來在調(diào)用getModuleFactory的時候區(qū)別模塊,如果這個屬性是undefined那么這個模塊將不會被注冊泼橘,如果有設置id,可以通過getModuleFactory()來獲取到當前模塊
jit? true 如果是true涝动,則當前模塊使用JIT編譯,否則使用AOT編譯炬灭。JIT: 即Just-in-time,動態(tài)(即時)編譯醋粟,邊運行邊編譯。AOT: Ahead Of Time重归,指運行前編譯米愿。

1.1.2 NgModule作用:

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

  • NgModule 用來控制組件育苟、指令、管道等是否可以使用椎木,處于同一個 NgModule 里面的組件默認互相可見违柏,而對于外部的組件來說,只能看到 NgModule 導出(exports )的內(nèi)容香椎,也就是說漱竖,如果你定義的 NgModule不exports任何內(nèi)容,那么外部使用者即使 import了你這個模塊士鸥,也沒法使用里面定義的任何內(nèi)容闲孤。

  • NgModule 是打包時候用到的最小單位,打包的時候會檢查所有 @NgModule 和路由配置,Angular底層是使用webpack打包讼积。因為Angular已經(jīng)幫我們配置好了webpack肥照,所以開發(fā)者輕松很多,否則就需要自己配置環(huán)境勤众。

1.2 @Directive

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

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

1.1.1 @Directive元數(shù)據(jù)解釋

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

inputs和outputs: 當前指令(組件)的輸入和輸出尸执。但是最新的Angular版本已經(jīng)不推薦使用這兩個屬性了,推薦使用@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綁定。如果綁定的值發(fā)生了變化葫督,Angular 就會更新該指令的宿主元素竭鞍。當 key 是宿主元素的 Property 時,這個 Property 值就會傳播到指定的 DOM 屬性橄镜。當 key 是 DOM 中的靜態(tài) Attribute 時偎快,這個 Attribute 值就會傳播到宿主元素上指定的 Property 去。對于事件處理:它的 key 就是該指令想要監(jiān)聽的 DOM 事件洽胶。 要想監(jiān)聽全局事件晒夹,請把要監(jiān)聽的目標添加到事件名的前面。 這個目標可以是 window、document 或 body丐怯。它的value就是當該事件發(fā)生時要執(zhí)行的語句喷好。如果該語句返回 false,那么就會調(diào)用這個DOM事件的preventDefault函數(shù)读跷。這個語句中可以引用局部變量 $event 來獲取事件數(shù)據(jù)梗搅。比如如下的代碼實例。

    @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>
       <!-- 動態(tài)組件存放的容器 -->
       <ng-template ad-host #test='adHost'></ng-template>
       <!-- 動態(tài)組件存放的容器 -->
       <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:配置將要注入到該指令(組件)中的一些查詢锭沟,內(nèi)容查詢會在調(diào)用 ngAfterContentInit 回調(diào)之前設置好抽兆。 試圖查詢會在調(diào)用 ngAfterViewInit 回調(diào)之前設置好。說白了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這是一個組件蝙斜。

? ? ? ?組件控制屏幕上被稱為視圖的一小片區(qū)域名惩。在組件類總定義組件的應用邏輯。在Angular應用中組件(Component)是屬于模塊(NgMoude)的孕荠。組件是比模塊更小的一個單元娩鹉。

1.2.1 @Component元數(shù)據(jù)解釋

? ? ? ?因為Component是繼承Directive的,Component也是指令的一種.所以指令能做到的事情Component也能做到.上面講的@Directive元數(shù)據(jù)都適用于@Component稚伍。

除了@Directive的元數(shù)據(jù)之外弯予,@Component元數(shù)據(jù)元數(shù)據(jù)還有如下屬性。

選項 類型 說明
changeDetection? ChangeDetectionStrategy 當組件實例化之后个曙,Angular 就會創(chuàng)建一個變更檢測器锈嫩,它負責傳播組件各個綁定值的變化。 該策略是下列值之一:ChangeDetectionStrategy#OnPush(0) 把策略設置為 CheckOnce(按需);ChangeDetectionStrategy#Default(1) 把策略設置為 CheckAlways
viewProviders? Provider[] 定義一組可注入對象呼寸,它們在視圖的各個子節(jié)點中可用
moduleId? string 包含該組件的那個模塊的 ID艳汽。該組件必須能解析模板和樣式表中使用的相對 URL。 SystemJS 在每個模塊中都導出了 __moduleName 變量等舔。在 CommonJS 中骚灸,它可以設置為module.id
templateUrl? string 組件模板文件的 URL。如果提供了它慌植,就不要再用 template 來提供內(nèi)聯(lián)模板了
template? string 組件的內(nèi)聯(lián)模板甚牲。如果提供了它,就不要再用 templateUrl 提供模板了
styleUrls? string[] 一個或多個 URL蝶柿,指向包含本組件 CSS 樣式表的文件
styles? string[] 本組件用到的一個或多個內(nèi)聯(lián) CSS 樣式
animations? any[] 一個或多個動畫 trigger() 調(diào)用丈钙,包含一些 state() 和 transition() 定義
encapsulation? ViewEncapsulation 供模板和 CSS 樣式使用的樣式封裝策略
interpolation? [string, string] 改寫默認的插值表達式起止分界符({{ 和 }})
entryComponents? Array<Type<any> | any[]> 一個組件的集合,它應該和當前組件一起編譯交汤。對于這里列出的每個組件雏赦,Angular 都會創(chuàng)建一個 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應用中轨蛤,管道的作用就是傳輸,可以把一個值通過某種變換成一個新的值虫埂。(其實就是對數(shù)據(jù)做進一步的處理)

1.3.1 @Pipe元數(shù)據(jù)

選項 類型 說明
name string 管道對應的名字祥山,在模板文件里面綁定管道的時候使用
pure? boolean 如果為true,則管道是pure的掉伏,這意味著只有在其輸入?yún)?shù)發(fā)生更改時才會調(diào)用transform()方法缝呕。管道默認是pure的澳窑。如果管道具有內(nèi)部狀態(tài)(即,結果取決于其參數(shù)以外的狀態(tài))供常,則將“pure”設置為false摊聋。在這種情況下,即使參數(shù)未更改栈暇,也會在每個更改檢測周期調(diào)用管道麻裁。

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>

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

<!-- 保留三位小數(shù)源祈,并且轉換成MB -->
<p>
  {{1024 * 1024 * 2 | fileSize:3:'MB'}}
</p>

1.4 @Injectable

? ? ? ?@Injectable修飾的類煎源,表示當前類是一個服務類。該類需要通過注入器根據(jù)服務提供者去創(chuàng)建服務對象香缺。然后給需要使用的地方使用手销。這里就涉及到Angular應用里面的依賴注入問題了。有興趣的可以參考图张。

選項 類型 說明
providedIn Type<any> | 'root' | null 指明當前服務注入的地方

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

2 屬性裝飾器

? ? ? ?屬性裝飾器:把裝飾器添加在屬性上祸轮,是屬性具有額外的一些功能兽埃。Angular系統(tǒng)里面屬性裝飾器有很多,如下:

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

@Input

? ? ? ?@Input: 負責把父組件的數(shù)據(jù)傳遞到子組件。然后在子組件里面做相應的處理痪蝇。這個就沒什么講的了,使用起來也簡單.

? ? ? ?Input裝飾器支持一個可選的參數(shù)冕房,用來指定組件綁定屬性的名稱躏啰。如果沒有指定,則默認使用@Input對應裝飾的屬性名耙册。不推薦為起別名给僵,推薦直接使用默認的名字。

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

@Output

? ? ? ?子組件暴露一個EventEmitter屬性饶辙,當事件發(fā)生時蹲诀,子組件利用該屬性emits(向上彈射)事件。父組件綁定到這個事件屬性弃揽,并在事件發(fā)生時作出回應脯爪。子組件的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:用于選擇當前組件引用的內(nèi)容(從ng-content中獲取元素) .一般在自定義組件的時候使用.

比如如下的代碼快骗,我們自定義一個組件ContentParentComponent.在這個組件里面通過@ContentChild獲取到ng-conent里面的內(nèi)容

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 讓使用該組件的人可以自定義里面的內(nèi)容 -->
    <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 參數(shù)裝飾器

? ? ? ?將裝飾器添加在參數(shù)上面方篮,一般都是構造函數(shù)的參數(shù)上。獲取注入器里面提供的服務對象俺叭。在咱們Angular框架里面注入器一般分為兩種:組件注入器恭取、模塊注入器。組件注入器里面注入的服務對象一般通過@Component裝飾器里面的provider元數(shù)據(jù)提供熄守。模塊注入器里面注入的服務對象一般通過@NgModule裝飾器里面的provider元數(shù)據(jù)提供蜈垮。

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

@Inject

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

@Optional

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

@Self

? ? ? ?從當前組件注入器里面查找依賴對象负间。再強調(diào)一遍是當前組件注入器里面查找偶妖,找到了就找到了,沒找到就沒找到政溃。

@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宿主關系)

自定義一個組件蟹倾,注意使用了<ng-content>

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() {
  }

}

自定義一個子組件,我們會把該組件放在<ng-content>對應的內(nèi)容里面

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對應的內(nèi)容</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裝飾器咱們就扯這么一些.主要是也為了讓大家在使用這些裝飾器的時候心里有個底.不同的場景用不同的裝飾器.如果大家在使用過程中有什么疑問鲜棠,可以留言.能力范圍內(nèi)盡量會解答的. 最好給出文章中的一些驗證代碼地址 https://github.com/tuacy/angular-decorator

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末肌厨,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子豁陆,更是在濱河造成了極大的恐慌柑爸,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,692評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盒音,死亡現(xiàn)場離奇詭異表鳍,居然都是意外死亡,警方通過查閱死者的電腦和手機祥诽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評論 3 392
  • 文/潘曉璐 我一進店門譬圣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人雄坪,你說我怎么就攤上這事厘熟。” “怎么了维哈?”我有些...
    開封第一講書人閱讀 162,995評論 0 353
  • 文/不壞的土叔 我叫張陵绳姨,是天一觀的道長。 經(jīng)常有香客問我阔挠,道長就缆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,223評論 1 292
  • 正文 為了忘掉前任谒亦,我火速辦了婚禮,結果婚禮上空郊,老公的妹妹穿的比我還像新娘份招。我一直安慰自己,他們只是感情好狞甚,可當我...
    茶點故事閱讀 67,245評論 6 388
  • 文/花漫 我一把揭開白布锁摔。 她就那樣靜靜地躺著,像睡著了一般哼审。 火紅的嫁衣襯著肌膚如雪谐腰。 梳的紋絲不亂的頭發(fā)上孕豹,一...
    開封第一講書人閱讀 51,208評論 1 299
  • 那天励背,我揣著相機與錄音砸西,去河邊找鬼。 笑死衅疙,一個胖子當著我的面吹牛鸳慈,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播绩郎,決...
    沈念sama閱讀 40,091評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼绿聘,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了兽愤?” 一聲冷哼從身側響起挪圾,我...
    開封第一講書人閱讀 38,929評論 0 274
  • 序言:老撾萬榮一對情侶失蹤哲思,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后帝簇,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體靠益,經(jīng)...
    沈念sama閱讀 45,346評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡胧后,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,570評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了纸巷。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,739評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡梯啤,死狀恐怖裆站,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情宏胯,我是刑警寧澤,帶...
    沈念sama閱讀 35,437評論 5 344
  • 正文 年R本政府宣布杭棵,位于F島的核電站魂爪,受9級特大地震影響,放射性物質發(fā)生泄漏艰管。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,037評論 3 326
  • 文/蒙蒙 一撩笆、第九天 我趴在偏房一處隱蔽的房頂上張望夕冲。 院中可真熱鬧裂逐,春花似錦、人聲如沸弥姻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至颠悬,卻和暖如春定血,著一層夾襖步出監(jiān)牢的瞬間澜沟,已是汗流浹背峡谊。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留濒析,地道東北人啥纸。 一個月前我還...
    沈念sama閱讀 47,760評論 2 369
  • 正文 我出身青樓斯棒,卻偏偏與公主長得像,于是被迫代替她去往敵國和親荣暮。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,647評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 模板表達式“{{}}”不能引用任何全局命名空間中的成員(如:window、document等等)的原因: 我想原因...
    科研者閱讀 963評論 2 4
  • core package 概要:Core是所有其他包的基礎包.它提供了大部分功能包括metadata迷扇,templa...
    LOVE小狼閱讀 2,576評論 0 3
  • 組件基礎 組件用來包裝特定的功能蜓席,應用程序的有序運行依賴于組件之間的協(xié)同工作。組件是angular應用的最小邏輯單...
    oWSQo閱讀 1,370評論 0 0
  • 聲明 本系列文章內(nèi)容梳理自以下來源: Angular 官方中文版教程 官方的教程祈秕,其實已經(jīng)很詳細且易懂雏胃,這里再次梳...
    請叫我大蘇閱讀 1,079評論 0 6
  • 人生在世,短暫不過百年方仿。多一份滿足,少一點抱怨仙蚜;多一份真誠,少一些虛偽呜师;多一份快樂贾节,少一些悲苦;多一份明白知牌,少一些...
    廣聰閱讀 213評論 0 0