我們這次來介紹"我"不常用的 inject decorator
@Optional, @Self, @SkipSelf, @Host
Injector 程式面說明 - 微模擬
非實際 Angular 的運作模式, 單純模擬一下 injector 大概的理念
在 Component / Directive 中我們都會這樣子將 service 注入進來
1 | export class AppComponent { |
這裡很簡單的假裝有一個 LogService
1 | class LogService { |
在 Angular 做 compile 的時候大概會將這些 service 放入到某個位置供 Injector
做使用
1 | const injector = new Injector([LogService, ..., ..., ..., ...]); |
我們來看一下 Injector 大概的長相
1 | class Injector { |
各 Component / Directive 要使用的時候透過 get 的方式將這些 service 拿出來
1 | class Component { |
程式碼: 點我
快速的了解 Injector 以後讓我們來看 Angular 關於 injector 的機制有哪些
多級注入器(Hierarchical injectors)
大致上分兩種類型注入:
- Element injector
- Module injector
Element injector
不是針對 module 去做 providers 設置, 而是在 Component / Directive 中的 meta 做 providers 設置, 這一類都屬於 Element injector
1 | ({ |
Module injector
我們常用的 providedIn: 'root'
就是屬於 module injector, 另外也可以針對 module 類型的檔案就行 providers 的設置, 這也是 module injector
1 | providers: [{ provide: LocationStrategy, useClass: HashLocationStrategy }]; |
以上兩種 injector 是有先後順序的, 先由 Component / Directive 透過 Element injector 一層層往上找, 都找不到以後就會切換到 Module injector 一層層往上找, 直到噴出錯誤
下圖可以很清楚的看這個 flow
以上的"微"模擬 Injector 是什麼, 以及 injectors 機制後就可以來介紹本篇的重點 Resolution Modifiers
@Optional
AppService 不設定 providedIn
1 | () |
在 Component / Directive 若要使用 AppService, 必須要在 meta 上定義 providers
1 | ({ |
若 Component / Directive 忘了定義 providers 就使用的話會出現錯誤, 這時候可以用 @Optional
將這個 provider 設位選用, 也就是在 compile 的時候不會屬於必須
1 | ({ |
@Self
依照 Hierarchical injectors 機制, 會在 Component / Directive 本身的 providers 開始往上尋找直到 root.
若只要查找 Component / Directive 本身的 providers, 就可以使用 @Self, 找不到就直接報錯
1 | ({ |
這些 Resolution modifiers 可以組合使用
@Optional() @Self()
@SkipSelf
依照 Hierarchical injectors 中的機制, 會在 Component / Directive 本身的 providers 開始往上尋找直到 root.
若要從上一層開始到 root, 也就是跳過自身這層, 就可以使用 @SkipSelf
1 | ({ |
@Host
這個是最為特別的一個 modifiers, 他不會遵循完整的 Hierarchical injectors 機制, 他只會查找 element 結構的範圍
下面可以看到 CardComponent 中的 template 結構, 包含 app-card-body
以及從上層來的 ng-content
app-card
內含app-card-body
app-card
包含上層來的app-card-test
1 | <div> |
app
使用 app-card
並且傳入 app-card-test
進去
1 | <app-card> |
app-card
設置了 CardService
1 | ({ |
app-card
內含的 app-body
如果使用了 @Host
是找不到 CardService
的, 因為 @Host
會看到自己本身, 和 @Self
行為一樣要定義自身的 providers
才行
1 | ({ |
app
是將 app-card-test
傳到 app-card
裡面, 所以透過 ng-content
會使 app-card-test
隸屬於 app-card
, 而不是被定義為 app-card
下面包含的內容, 隸屬的意思可以看做就是這個 app-card
本身的實例, 所以 @Host
等同於 app-card
, 這時候就可以使用 app-card
提供的 CardService
了
1 | ({ |
總結
Angular 提供這四個 modifiers, 可以在不同情境中精準的設定出欲使用 provider, 但是一般的情況比較少會用到, 有個概念就好, 這樣在觀看網上的技術文章比較可以融會貫通, 而不是又多了一些看不懂的內容.