iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 1
3
Modern Web

從巨人的 Tip 看 Angular系列 第 1

[Day 1] 透過 DI 讓 children 與 parent 互動吧!之你知道 Angular 怎麼注入 component 的嗎?

大家好我是 EP,這次的系列主要會收集一些 Angular 在開發上會使用到的技巧(或稱 Tip),並從這些技巧作為切入點,然後直接開始爬 Angular 的原始碼!

所以文章的組成會是先介紹 Tip,再來就是爬完原始碼的分析結果。

本系列文章會以 Angular 10 作為主要的作業環境,並使用 Ivy + AOT 來編譯應用程式。


直接進入文章本體!

今天要分享的 Tip 是:

在 child component 內透過 DI 的方式與 parent component 互動!

@Component({
  selector: 'app-parent',
  styleUrls: ['./parent.component.scss'],
  template:`
<p>parent works!</p>
<app-child></app-child>
`
})
export class ParentComponent {
}

@Component({
  selector: 'app-child',
  templateUrl: './child.component.html',
  styleUrls: ['./child.component.scss']
})
export class ChildComponent implements OnInit {

  constructor(private parent: ParentComponent) {
    console.log(`⚡: ChildComponent -> constructor -> parent`, this.parent);
   }

  ngOnInit(): void {
  }

}

在 Angular CDK 的 Tree 以及 Stepper 其實就有用到這樣在 child component 內注入 parent component 的設計方式,一方面可以確保開發上不會誤用需要有 parent component 的元件,另一方面也可以確保所有 children 都可以取得相同的 parent。

你有想過嗎?

我們一般認為要提供 DI 服務都需要選擇在 module 提供 Providers 屬性、或是在 @Injectable() decorator 內提供 ProvideIn 屬性,才能讓 Angular 的 DI Framework 幫你注入到其他元件內,但今天所分享的這個 Tip 的卻不需要做任何額外的設定,Angular 就幫我們注入了對的物件。

如果你曾經仔細看過 Angular - Navigate the component tree with DI 這份文件的話,會發現這份文件有著輕描淡寫的一句話:

There is no public API for acquiring a parent reference. However, because every component instance is added to an injector's container, you can use Angular dependency injection to reach a parent component.

翻譯就是「我大 Angular 會幫你把每個 Component 都放進 Injector 內!你只要用就對了!」

不行,這我沒辦法接受。所以就來爬 code 囉!

一言不合就爬 code

先來看一下 Angular 建立 AppComponent 的順序:

https://ithelp.ithome.com.tw/upload/images/20200917/20129148ZSITtKTiqf.png

圖一↑

接著來看一下 createRootComponentView 這個方法:

export function createRootComponentView(
  // ...略

  if (tView.firstCreatePass) {
    diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, rootView), tView, def.type);
    markAsComponentHost(tView, tNode);
    initTNodeFlags(tNode, rootView.length, 1);
  }

  addToViewTree(rootView, componentView);

  // Store component view at node index, with node as the HOST
  return rootView[HEADER_OFFSET] = componentView;
}

其實重點也只有呼叫 diPublicInInjector第 199 行而已,透過這個方法,Angular 就會將 component 實體加到 injector,而 child component 就能透過 DI 的方式取得 parent comopnent。

值得注意的是,在這個範例中只有 AppComponent 會透過 createRootComponentView 方法來呼叫 diPublicInInjector,其餘的 ParentComopnent 或是 ChildComponent 都是透過 elementStartFirstCreatePass → resolveDirectives 這樣的流程來呼叫 diPublicInInjector,至於為什麼會有這樣的差異我們可以看一下編譯過後的 main.js:

https://ithelp.ithome.com.tw/upload/images/20200917/20129148YUCj3AP7SB.png

↑圖二

只看反白的重點就好,因為 ParentComponent 是被嵌在 AppComponent 內的,所以 Angular 會改用 ɵɵelement 方法來建立 DOM element 以及其他 Angular 所需要的物件,而不是採用與建立 AppComponent 相同的方式來產生 ParentComopnent。

而 ChildComponent 也是用一樣的方式被 ParentComponent 建立的:

https://ithelp.ithome.com.tw/upload/images/20200917/20129148wpBu71XsUc.png

↑圖三

以上就是如何透過 DI 的方式讓 children 與 parent component 互動的方式以及 Angular 如何將 component 加入 Injector 的實作細節,而接下來的幾天,我會繼續延伸下去,分析 children 是怎樣在茫茫人海中找出 parent。

直接進入業配主題!

這次是我第一次參加鐵人賽,也抱著不想當拖油瓶的心情加入了「全端開發人員天梯」團隊跟著一起爬天梯,以下按照入團順序列出我們團隊夥伴的系列文章!


下一篇
[Day 2] 深度探討 Angular 將 component 加入 Container 的流程
系列文
從巨人的 Tip 看 Angular30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
scertgogo
iT邦新手 5 級 ‧ 2020-09-17 21:30:22

圖都沒引用成功喔!!

感謝通知!
因為我這邊看是正常,看來應該是圖片請求時會帶瀏覽器驗證資訊

我要留言

立即登入留言