作為實現Angular最為強大的資料管理功能的管道,Input與Output可以說是建構起Angular開發生態最主要的基礎
他在開發實作的情境上很大程度的幫我們隔離開對於資料的變化操作,而是讓資料得以用"流"的狀態進行處理
每一個pipe與component則像是這龐大的資料流中的引流渠與水力發電所,透過資料的格式化導至所需實踐的功能,進而讓系統資料與使用者操作情境發揮最大效能
Input在剛剛的敘事之中就像是水壩的出水口,透過洩洪將資料流由父Component傳遞至下一個集水區(使用者操作情境 a.k.a 子Component),然後透過Component的Method實踐使用者所需功能
Output則是將使用者操作後的成果傳遞回父Component中,Output可以不依賴於Input所提供的資料,單純在Component中所操作後的成果都能透過這個管道讓父Component拿到我們預設中所想要的資料。
這個就涉及到了Angular對於資料的處理邏輯與概念,以下分別有功能對全域的全域處理、父功能對子功能單層處理、功能對功能的平級處理
component v.s. 全域
父component v.s. 子Component
子Component v.s. 子Component
全域的處理情境在這邊一樣先給個關鍵字store,讓我抖抖書袋子然後之後說
在這裡我們先聚焦於今天的主題Input()和@Output()是如何在單層與平級的資料處理。
在前端功能越來越複雜化的現在,如何斷捨離那些資料就變為一個非常重要的學問了,而我現在也很努力的學習中QQ
我們可以透過@Input()
這個裝飾器進行Input管道的鑿開工程,範例如下:
export default class GoodDetailComponent {
@Input()
goodID: string;
}
我在這邊的邏輯是在商品陣列中,我可以僅透過傳遞一個GoodID進這個子Component讓他去渲染生成我想要給消費者所看到的商品明細。
在這裡input就非常地像我們在JS的function所宣告的入參與C#Class的傳參,我們透過傳遞一個值並期望這個值在我們的函式或方法中有所作用
我們現在有一個開鑿出來的管道了,那接下來就要往裡面注入靈魂,我是說,資料
<app-good-detail [goodID]="selectedGoodItem.ID"> </app-good-detail>
透過這樣子我們就能在子Component中放入我們想要放的資料
進到 <app-good-detail / >
後,我們就可以在 GoodDetailComponent
中以 this.goodID
去做各式各樣的功能實現
那假如我的資料有變化的時候呢? 我的選擇商品進行變化的話,我的子Component會有什麼樣子的變化呢?
這邊就來到了每個Component都會遇到的生離死別問題 - 生命週期
Angular 框架中的生命周期是一個重要的概念,它指的是在 Angular 組件中發生的一系列事件或鉤子(hooks)。這些事件允許你在組件的不同階段執行代碼,以管理和控制組件的行為。以下是 Angular 組件的生命週期事件:
ngOnChanges:當組件的輸入屬性(Input properties)發生變化時觸發,這個鉤子用於響應外部數據的變化。並且不受次數的限制,一旦渲染之後在每次的不同Input有資料變化值都會觸發,假如有複數管道而僅一資料有異動時,其他資料不會被強制更新成undefiend
或null
ngOnInit:當 Angular 初始化組件時觸發。通常在這個鉤子中進行初始設置,如設置默認值或發起數據請求。並且僅觸發一次。之前在需要透過constructor注入Service的時候,總會很想在constructor進行一些方法的操作,但以整個生命週期來說的話,在這裡的操作是在Component"渲染之前",換言之涉及到一些渲染剛開始與結束的圖像操作其實都不應該在這裡去進行,例如loading的遮罩。而一些component所需參數,from store或入參或需要再透過呼叫API取得的資料則可以在這時候進行宣告
ngDoCheck:每當 Angular 檢查並重新檢查組件的狀態時觸發。通常與Change Detection機制一起使用,用於自定義檢查和更新邏輯,並且不受次數的限制。
ngAfterContentInit:當 Angular 投影內容(ng-content)到組件視圖後觸發。並且僅觸發一次。在這裡可以處理投影內容的初始化邏輯。剛剛上述我說到的loading遮罩就適合在這裡進行
ngAfterContentChecked:每當 Angular 檢查投影內容的變化時觸發。通常與投影內容的變化檢測一起使用。
ngAfterViewInit:當 Angular 初始化組件的視圖後觸發。並且僅觸發一次。用於處理與DOM操作相關的邏輯。loading遮罩適合在這裡進行關閉
ngAfterViewChecked:每當 Angular 檢查視圖的變化時觸發。通常與視圖的變化檢測一起使用。
ngOnDestroy:當組件即將被銷毀時觸發。並且僅觸發一次,理論上也只能觸發一次,想要下次下次再來。這裡可以執行一些清理工作,如取消訂閱和釋放資源。
在我們之前講到ngIf的時候有說到重新選染的狀況對八? 每一次的!boolean
都會讓整個Component進行渲染,換言之假如有一些操作者情境資料尚未處理完畢就被切換狀態導致重新渲染的時候,那些資料是不會被保存下來的
也因此在那時候有說到不涉及到資料安全性與僅較少的資源來處理渲染的時候,我自己個人會傾向於使用[hidden]與ngClass進行在畫面上的顯示與否。
為什麼會有這麼多關卡或是鉤子還看資料流中的狀況呢? 這主要就是涉及到Angular的核心概念了
這一切都要從雙向資料流開始說起
資料流雙向綁定(Two-Way Data Binding)是一種前端開發概念,它允許應用程序的使用者在使用者界面(通常是表單元素)中輸入數據,同時也將該數據自動同步到應用程序的數據模型中,反之亦然。這意味著當使用者在界面上的輸入發生變化時,數據模型會自動更新,反之亦然。
這種技術使開發者能夠輕鬆地處理用戶輸入和應用程序的數據狀態,而不需要手動監聽輸入事件並更新數據。
假設我們有一個簡單的登錄表單,其中包含一個輸入欄位用於用戶名。當用戶在輸入欄位中輸入名稱時,這個名稱將同時在界面上顯示並保存到應用程序的數據模型中。如果應用程序的數據模型中的名稱屬性發生變化,界面上的輸入欄位也會自動更新以反映這一變化。
最後的最後則是來說說Output吧
在Angular中,@Output 裝飾器和事件繫結(Event Binding)一起使用,用於向父組件發送數據或觸發自定義事件。它允許子組件向其父組件傳遞信息或通知父組件某些事情已發生。
定義一個 @Output 屬性:在子組件中,我們需要定義一個具有 @Output 裝飾器的屬性,該屬性將用於觸發事件。例如:
export class GoodDetailComponent {
@Output() addToCart: EventEmitter<string> = new EventEmitter<string>();
sendDataToParent() {
this.addToCart.emit(selectedItemGroup);
}
}
同時我們們在外層需要接上這個事件的水管,才不會變成壁癌或水花四濺
<app-good-detail (addToCart)="updateCartList($event)"> </app-good-detail>
當啷~這樣就完成水管的串接啦
相比起input的狀況Output的狀況就簡單了許多,大部分僅需專注於將資料遞出來,遞出來之後的所作所為就由父Component進行管理與後續所需實踐功能。