Main reference: Angular 官方網站
Main reference: Angular 大師之路
Main Reference: Book - 圖像 Angular 開發入門
Related Reference: Book - 打通 Rxjs 任督二脈
retry
重新嘗試,錯誤就讓他進到錯誤
範例:
(參考程式碼: https://stackblitz.com/edit/rxjs-5q2cxq?file=index.ts)
retryWhen
retryWhen 內部需要設計一個 notifier callback function,retryWhen 會將錯誤傳到 notifier callback function,同時需要回傳一個 Observable 物件,retryWhen 會去訂閱他
白話文: 回傳的 Observable 有訂閱發生,重新嘗試,直到 Observable 訂閱結束,才會停止重新嘗試。
需要注意的是:
retryWhen 內傳入一個 callback,這 callback 有一個參數會傳入一個 observable,這個 observable 不是原本的 observable(example) 而是"例外事件送出的錯誤所組成的一個 observable",等到這次的處理完成後就會重新訂閱我們原本的 observable。
假設我們一遇到 error 就會 delay 再嘗試
圖解會是以下這樣的:
return complete 情況
範例:
(參考程式碼: https://stackblitz.com/edit/rxjs-q1oapr?file=index.ts)
return error 情況
範例:
但說實話這樣有一點多此一舉,因為例外處理通常是使用 catch 而不是 retryWhen, retryWhen 通常是用來蒐集 error 並通知有 error,如下:
(參考程式碼: https://stackblitz.com/edit/rxjs-sojppc?file=index.ts)
Main reference: Angular 官方網站
Main reference: Angular 大師之路
Main Reference: Book - 圖像 Angular 開發入門
分類介紹:
輸出資料:
回應事件:
說明:
文字插值 {{}} 的方式可以讓我們將程式內的屬性值繫結在頁面上。
範例:
在 html 中這樣寫:
在 ts 中這樣寫:
(參考程式碼: https://stackblitz.com/edit/angular-ivy-npvs8c?file=src/app/app.component.ts)
事件繫結 Event Binding
用途:
想要監控使用者動作,讓係寵在觸發特定動作時,可以執行指定的元件方法
在 html 中這樣寫:
在 ts 中這樣寫:
實際範例
需求:
監測使用者 click button,如果按下左邊的 button, fontSize + 1 (需要定義一個 function), 如果按下右邊的 button, fontSize - 1 (需要定義一個 function)
(參考程式碼: https://stackblitz.com/edit/ng-book-event-binding-rqzpsu?file=src/app/app.component.ts)
屬性繫結
在 html 中這樣寫:
Property binding
用途:
繫結 HTML 標籤下的 DOM property
需求:
想要改變 property 屬性值,此例子是在 fontSize 小於等於 10 的 情況下 "減少1pt" 的 button 都被 disable
範例:
(程式碼參考: https://stackblitz.com/edit/ng-book-property-binding-7cqqg3?file=src/app/app.component.ts)
Attribute binding
注意事項:
若改成使用 attribute 繫結,需要考慮他的屬性值不一定與 binding 相同,就像是 attribute 的 disable 是以 disabled 或是 null 為值,不像是 property 是用 disabled: true / false
使用方式範例:
相同需求,換成 attribute 寫法:
(程式碼參考: https://stackblitz.com/edit/ng-book-attribute-binding-4gne8g?file=src/app/app.component.html)
樣式繫結 style binding
在 html 中這樣寫:
需求:
想要根據屬性數值改變樣式,根據 fontSize 改變實際的 fontSize 樣式 和 color
如果想要針對特定的樣式做 binding,例如針對 pt 值:
(參考程式碼: https://stackblitz.com/edit/ng-book-single-style-binding-moqmdg?file=src/app/app.component.html)
類別繫結 class binding
需求:
我們元素的樣式變得很複雜,需要用到 class 對應 style,希望可以透過實際切換 class name 的方式來讓樣式變換
以下例子我們想要切換文字的大小,我們有小中大三種可以選擇:
(參考程式碼: https://stackblitz.com/edit/ng-book-class-binding-7ddjsd?file=src/app/app.component.ts)
雙向 binding
盒子裡的香蕉:
在 template 實現雙向綁定的語法為 [(…)] ,又稱為banana-in-box語法(盒子裡的香蕉)。
NgModel 的意涵:
[ (NgModel) ] = ngModel 的 property binding + ngModelChange 的 event binding
使用注意事項:
記得在 app.module.ts imports FormsModule,再使用 NgModel
範例: 雙向綁定的實作方式
採用 [(NgModel)]
:
採用分開的 NgModel & NgModelChange
不採用 NgModel
程式碼備註:
$any 說明:
$any() can be used in binding expressions to disable type checking of this expression
(參考程式碼: https://stackblitz.com/edit/angular-ivy-3fwwkp?file=src/app/app.component.html)
利用 @Input 來接收資料 (子元件接收父元件的資料)
用途:
input 為可以變動的元素,我們想要透過父元件將資料傳下去給子元件,讓子元件來接收資料
範例:
(程式碼參考: https://stackblitz.com/edit/ng-book-input-property-rgfmhr?file=src/app/font-size/font-size.component.ts)
利用 @Output 來傳送資料 (通知父元件要做什麼事情)
用途:
Output 為告知父元件有事情發生,所以由父元件去定義要做什麼事情,兒子元件要去定義需要被繫結的 Output 事件
範例:
需求:
想要讓父元件的 fontSize 也會跟著變動,所以我們需要通知父元件
(程式碼參考: https://stackblitz.com/edit/ng-book-output-event-qyskqk?file=src/app/font-size/font-size.component.html)
問題:
面對重複使用的程式碼,如果我們使用一直複製貼上的方式會不太好,假設我們想要改一個地方的程式碼而其他地方沒改到豈不是GG?
封裝範本 solution:
外部元件使用時決定元件使用內容 solution (動態投影):
在使用 ng-content 的元件內,取得特定外部由外部傳來的範本參考變數或子元件的實體時,可以用 "@ContentChild" 裝飾器來完成:
白話文:
要從子元件的 ts 裡,抓到父元件 html 裡的子元件裡的 content
也就是說假設有一個父元件如下:
子元件如下:
ts 如下:
取得外部元件實體的方法(單個):
加入 ContentChild directive 屬性,會在 ngAfterContentInit() 方法中依條件取得元素實體,此方法只會在 onDoCheck() 後被觸發,且只會觸發一次。
備註: ContentChildren 取得多元素的實體,會得到一個包含全部實體的 QueryList 泛型屬性
ngAfterContentInit() -> ngAfterContentChecked()
變更檢測 -> ngDoCheck 後觸發 ngAfterContentChecked()
取得外部元件實體的方法(多個)
此處的 descendants true 來決定搜尋子元素以及後代所有元素
ViewChild 使用方式:
在父元件中:
指定第二個選擇性參數內的數值,決定我們什麼時候可以在特定鉤子方法中取得實體
此處通知 ngOnInit() 取得所需要的頁面元素實體
圖解:
ngOnChanges - 呼叫時機點: ngOnInit 之前被呼叫、元件輸入性值 input 發生變化時呼叫
注意! 若輸入值是 Object (紀錄reference 位置),我們又只有改變物件的屬性的值時,並不會改到 Component 所記錄的參考位置 -> 可以使用 ngDoCheck() 檢查
(參考程式碼: https://stackblitz.com/edit/ng-book-input-property-rgfmhr?file=src/app/app.component.ts)
ngOnInit - 呼叫時機點: ngOnChanges 被呼叫後 -> ngOnInit 被呼叫一次,在總體的 LifeCycle 只會被呼叫一次
constructor 和 ngOnInit
實務上習慣讓元件屬性值的初始化作業在 constructor 和 ngOnInit 鉤子方法中進行,而讓元件類別的建構式負責注入服務 constructor(private ..Service: service) 的工作。
ngDoCheck - 呼叫時機點: 變更檢測週期其實都會呼叫 ngDoCheck
更詳細的內容可以參考李梅的文章:https://limeii.github.io/2019/06/angular-ngdocheck-onpush-strategy/
ngAfterContentInit - 呼叫時機點: 把外部內容投影至元件/文件的內容之後呼叫
ngAfterContentChecked - 呼叫時機點:觸發 ngAfterContentInit() 後觸發,除此之外也會在 ngDoCheck() 方法之後被觸發
ngAfterViewInit - 呼叫時機點:加入 @ViewChild 裝飾器屬性,當頁面載入時會在 ngAfterViewInit() 鉤子方法中一條件取元素實體,鉤子方法會在 onAfterContentChecked() 方法後觸發,且只會觸發一次
備註: 若要在 ngInit() 就先取得所需要的頁面實體,我們可以在 ViewChild 上設定第二個參數
!!注意:但在針對含有 *ngFor、*ngIf 等指令的情況,無法在ngOnInit() 鉤子方法時取得實體
ngAfterViewChecked - 呼叫時機點:會在 ngAfterViewInit() 後被觸發,在變更監測時則會在 ngDoCheck() 時被觸發
ngOnDestory - 呼叫時機點: 使用者離開頁面,Angular 會銷毀頁面內的元件與指令實體,而在銷毀之前會觸發 ngOnDestroy()
實務上釋放的資料大多包含:
(10/18 報告範圍)
需求:
為元件 (selector 指定的 tag) 加上樣式
例如: 我們想要為 "<app-demo></app-demo>" 加上樣式
host:
針對該元件對象進行樣式設定
:host(.target_style)
指定使用 target_style 為 className 的才會套用
:host-context(.target_class / empty)
會一直向上層尋找
(參考程式碼: https://stackblitz.com/edit/ng-book-host-selector-4jknfk?file=src/app/demo/demo.component.css)
需求
想要父元件透過資料繫結去設定或是接受屬性資料,會使用到範本參考變數。
可以在 html 那邊直接取得並使用他
使用方式範本:
父元件
子元件
(參考程式碼連結: https://stackblitz.com/edit/angular-ivy-xbtqke?file=src/app/app.component.html)
備註說明:
使用說明
範例:
#tinputValue 的值會是這個 input 的 DOM 物件,我們可以直接操作 input DOM 物件
(參考程式碼: https://stackblitz.com/edit/angular-ivy-xbtqke?file=src/app/app.component.ts)
Related reference:
https://blog.typeart.cc/dive-deeper-service-in-angular/
Related reference:
https://www.gss.com.tw/blog/angular-26-深入-service-3
依賴注入 DI - Injectable & providedIn
需求:
希望整個應用程式共用該 service
範例:
上述寫法意味著:
為應用程式注入級別 (root) 的 Service,也就是整個應用程式都可以注入,是在整個 Angular 應用中提供單例 (singleton) 服務,避免產生多個實例
(備註: singleton 意味著 "在注入器所屬模組內,相同的服務最多只會有一個實體,Angular 會透過裝飾器的定義,將未使用的依賴對象進行搖樹優化而排除。")
上述 providedIn: 'root' 的寫法同在 app.module.ts 中的 provider 後面填上: 'xxxService' (範例為 TaskService)
同等程式碼如下:
Injectable single module
需求:
不想讓服務給整個應用程式共用,想在單獨的模組中使用或是在單獨的 Component 中使用
核心概念:
遇到需求變更時,優先考慮加入新的程式,而非改動舊的程式
服務抽換的方式:
useClass
用途:
轉換 Service provider 的一種方式
[情境題] 假設今天的 service 原本是原價,變成全面九折,我們可以使用 改變 NgModule provider 的方式 將原本的 service provider 與 另外一個新的 service 做 useClass 替換
(參考程式碼: https://stackblitz.com/edit/ng-book-provider-class-gkzpxu?file=src/app/app.component.ts)
useExisting
用途:
抽換服務,與 useClass 的不同在於,此使用的方式是用原本的實體,而不會重新再建立實體,若不存在任何實體就會拋出例外
以下為不會拋出例外的寫法:
以下為會拋出例外的寫法:
(參考程式碼: https://stackblitz.com/edit/ng-book-provider-class-gkzpxu?file=src/app/app.module.ts)
useValue
需求:
如果替換掉的邏輯不混複雜,也可以使用 useValue 抽象服務,透過 useClass 來替換類別
(參考程式碼: https://stackblitz.com/edit/ng-book-provider-class-gkzpxu?file=src/app/app.component.ts)
useFactory
需求:
當我們需要在不同情況下 return 不同 service 時,我們可以使用到 useFactory
若我們需要將外部服務來做判斷或是注入到訂單服務內,我們可以使用useFactory 將 deps 中指定的外部服務傳入 useFactory
(參考程式碼: https://stackblitz.com/edit/ng-book-provider-factory-cfyysv?file=src/app/app.module.ts)
指令是透過 @Directive 來定義
ts:
html:
指令有分為管理樣式的屬性指令和變更DOM佈局的結構指令:
指令 (Directive) 包含了屬性型指令 (Attribute Directive) 與結構型指令 (Structural Directive) 兩種,前者用於改變 DOM 元素的外觀與行為,後者則是操控 DOM 樹,透過新增、移除或替換 DOM 元素來修改頁面結構。
使用內建指令前需要先引入 CommonModule 模組
介紹:
*ngFor 為 Angular 提供的語法糖
範例為:
實際上 *ngFor 會轉換成:
而他轉換的方式就是將 *ngFor 轉換成 ng-template 並且把宿主元素放置其中,也就是會轉換成如下:
ngFor 提供的變數:
index , first , last , odd , even 等變數
透過這些變數,我們可以針對這些變數做一些 class binding 來做樣式設定
範例:
可以針對奇數的 task 加上特定的樣式
ngFor 的使用注意事項:
若使用 *ngFor 而為進行其他設定,當來源被重設時就會 DOM 會被重新渲染,聽起來效能很不好對吧?
解決方式:
可以設定 trackBy 來追蹤已經更改的項目,讓 Angular 可以只重新渲染他們
使用方式:
(參考程式碼: https://stackblitz.com/edit/ng-book-ngfor-trackby-directive-4jd2am?file=src/app/app.component.html)
(參考網站: https://ng-book-ngfor-trackby-directive-4jd2am.stackblitz.io/ 開啟開發者工具)
需求:
一個元素只能使用一個結構指令,不能在同個元素內同時使用 *ngIf 又使用 *ngFor,但若不想為了建立一個結構指令而多了 DOM,可以使用 ng-container
使用情境:
針對動態元件的部分,有時候會需要比較複雜的載入,例如在不同條件下,我們想要顯示成一般的文字、圖片或影片,這樣會增加範本的程式複雜度,這時侯可以使用 *ngComponentOutlet 來依照條件動態載入特定元件。
使用方式就參考:
https://stackblitz.com/edit/ng-book-componet-outlet?file=src%2Fapp%2Fapp.component.html
範例:
可以在 ts 裡面針對 component 去做切換
方便的地方在於,這也適用於 *ngFor,可以將 let task of tasks 後面加上 | slice start_number : end_number
Reference:
https://blog.kevinyang.net/2020/02/13/angular-keyvaluepipe/
ObjectPipe
Related refernce: https://ithelp.ithome.com.tw/articles/10209035
建立 Component
設定指定頁面以及路由
(備註: router-outlet 功用: 用來存放被 route 的 Component)
建立路由
切換路由頁面可以透過 Router 方法:
or
若需要路由參數,需要透過 ActivatedRoute 內的 snapshot 屬性,取得路由變數
範例:
參數說明:
RouterLink 是一種 Directive:
當應用於範本中的元素時,使該元素成為開始導航到某個路由的連結。導航會在頁面上的 <router-outlet> 位置上開啟一個或多個路由元件。
snapshot
路由引數與你在此路由中定義的路徑變數相對應。要訪問路由引數,我們使用 route.snapshot,它是一個 ActivatedRouteSnapshot,其中包含有關該特定時刻的活動路由資訊。
Reference:
Template-Driven Form 與 Reactive Form 的差異:
https://ithelp.ithome.com.tw/articles/10195280
分類 - Template-Driven Form & Reactive Form:
Form 功能 \ Form 種類 | Template Driven Form | Reactive Form |
---|---|---|
組件驗證控制寫法 | 寫在<input> or <select> 標籤內,並利用 ngModel 來確認是否輸入了能驗證通過的內容 | 通常使用 Controller 來做驗證 (寫在 ts) |
同步 or 非同步 | 非同步 | 同步 |
同步 or 非同步原因 | 個表單元件透過 directive 委派檢查的功能 | 任何一個節點可以取得表單資料,且資料是同步被更新的 |
Module | FormsModule | ReactiveFormsModule |
Control 實體 | 透過 NgModel、NgModelGroup 建立 FormControl、FormGroup 表單型別實體 | 開發者自行宣告與建立表單類型實體 |
建立 Form 的步驟:
確認表單邏輯是否複雜 ? 若複雜又需要以方便的方式處理同步資訊的問題 (跨欄位更新之類的),可以使用 Reactive Form,反之,則可以使用 Template Driven Form
Template Driven Form
使用基礎類別
把表單欄位的 Type 定義好 - 建立 interface
範例:
建立 input value 與 model 屬性的關係 - ngModel 資料繫結
把範本驅動表單中的控制元件繫結到資料模型中的屬性,如果在元素上使用 ngModel 時,必須為該元素定義一個 name 屬性。Angular 會用這個指定的名字來把這個元素註冊到父 <form> 元素上的 NgForm 指令中。
範例:
追蹤整個 Form 的資料驗證狀態 -
ngForm 追蹤整個表單資料的狀態 (設立範本樣式變數以讓其他 view 元素可以追蹤到整體表單的狀態,例如: form 是否 invalid):
只要 import FormModule,就會建立一個最上層的 FormGroup 實例,並把它繫結到 <form> 元素上,以追蹤它所聚合的那些表單值並驗證狀態。
範例:
驗證表單 style
驗證表單操作
submit 數值後的操作
(UI 顯示: 點選 Submit 按鈕後,submitted 標誌就變為 true,表單就會消失。接著會顯示已經 submitted 的資料。)
範例:
(參考程式碼: https://stackblitz.com/edit/angular-rujzjq?file=src/app/hero-form/hero-form.component.html)
Reactive Form
FormControl
在單元表單元件中檢查值並且驗證狀態 ( input、select 等等)
FormArray
以索引方式追蹤表單驗證狀態
FormGroup
一組值與驗證狀態 (FormControl),一個 form 表單就是一個 FormGroup。
範例:
html:
需要屬性綁定 formGroup 與 要關聯的 form name (範例為 form),在 input 等元件需要設定 formControl 的 name (formControlName) 以方便未來追蹤元件狀態。
ts:
(上述範例參考程式碼: https://stackblitz.com/edit/angular-ivy-bk3bkq?file=src/app/app.component.ts)
(10/20 sharing 範圍)
Reference:
依賴反轉原則 - Dependency Inversion Principle
Angular DI
開放封閉原則 - Open-Closed Principle
SOLID 設計原則:
依賴反轉原則 DIP
高層模組不應該依賴低層模組,兩者應該依賴抽象。
開放封閉原則 OCP : 利用依賴的提供者抽換服務
核心: 一個軟體製品應該對於擴展是開放的,但對於修改是封閉的。
核心解說:
軟體在面對擴展時,應該是開放的且擴充時不應該修改到原有的程式。
範例:
總結 Provider 相關的命題:
@NgModule 裝飾器的提供者 (provider) 陣列內,在此定義的依賴對象則皆會被封裝至模型內。 這個行為是在做配置可注入的服務,是屬於 DI 範圍。
但若是著重在不同 service 彼此之間的抽換擴充,這種設計模式是屬於 "開放封閉原則"
Reference:
Main reference:
https://indepth.dev/posts/1305/the-last-guide-for-angular-change-detection-youll-ever-need
https://hackmd.io/@sherman/SJxW6tuT-w
Related reference (Zone.js):
https://ithelp.ithome.com.tw/articles/10208831
https://www.php.cn/js-tutorial-488503.html
Related refernce (Inline Caching):
https://mrale.ph/blog/2012/06/03/explaining-js-vms-in-js-inline-caches.html
Related reference: (change detection)
https://ithelp.ithome.com.tw/articles/10209026
changeDetection: ChangeDetectionStrategy.OnPush
(參考程式碼: https://stackblitz.com/edit/ironman2019-change-detector-ref-gi67zw?file=src/app/app.component.ts)
什麼是變更偵測?
框架藉由組合狀態 Model & 範本,複製 app 狀態到 UI 上,若狀態變更,也必須要更新 DOM ,這種將 HTML 與資料同步更新的機制稱作 "變更偵測 Change Detection"
白話文: 資料改變更新 DOM 的過程
變更偵測的運作過程?
GIF 圖解:
Zone.js
用途:
Angular 的變更偵測方式是採取 "當非同步事件發生後,進行變更偵測"
(這些非同步的例子如 Http Request、setTimeout 等),而 Angular 包裝 zone.js 程式來讓我們可以透過他得知所有事件的發生,並且觸發變更偵測,這個工具也就是 " NgZone " 服務
要注意的一點是: 一個 Angular 應用中只有存在一個 Angular Zone,每個 task 都會在 Angulra Zone 中執行。
白話文: zone 可以保持追蹤並攔截任何非同步的 task
zone 通常有這些階段
stable 穩定
狀態unstable 不穩定
stable 穩定
會觸發 Change Detection 的事件:
範例: (onUnstable & onStable 的範例)
(參考程式碼: https://stackblitz.com/edit/angular-ivy-dvghpm?file=src/app/app.component.ts)
runOutsideAngular()
當我們在會觸發變更偵測的狀態下想要執行一些跟畫面無關的程式時(例如某個數字加一或是呼叫一個 API 等等,但不會影響畫面),可以把程式放在 runOutsideAngular(),來避免發生變更偵測造成的效能耗損:
注意:
runOutsideAngular 內所執行的 function 是不會觸發任何 change detection 的 (此處以一個延遲執行的 function 來進行測試)
(參考程式碼: https://stackblitz.com/edit/angular-ivy-qzk9ki?file=src/app/app.component.ts)
run()
跟 runOutsideAngular 相反,如果在程式中「不小心」脫離了 Angular 變更偵測的範圍,像是使用 jQuery 或其他第三方與 DOM 操作有關的套件時,很容易不小心就脫離變更偵測了,這時候可以用 run() 方法來讓程式回到 Angular 變更偵測內。
變更偵測策略
Default
Default 介紹
設定:
Angular 預設使用 ChangeDetectionStrategy.Default 的變更偵測策略。
設定範例:
變更偵測過程:
當每一次事件觸發變更偵測時(例如使用者事件、timer、XHR、Promise),會由上到下檢查元件樹中的每一個元件。(dirty check)。
存在的問題:
在較大的 app 中可能會降低效能,因為含有太多元件。
OnPush
OnPush 介紹
設定:
我們可以用在元件裝飾器的 metadata 中增加 changeDetection 屬性,並設定為 ChangeDetectionStrategy.OnPush。
變更偵測過程:
這個變更偵測策略可以對這個元件和子元件忽略非必要的檢查。
變更偵測的時機:
(元件)輸入的參考改變
Angular 會在 @Input() 的資料改變時執行變更偵測。
使用 OnPush 策略,只會在 “屬性的值有新的參考” 時才會觸發。
因此在採用 OnPush 機制時,我們修改物件屬性不會建立新的參考,所以不會觸發變更偵測。
(參考程式碼範例: https://stackblitz.com/edit/angular-ivy-c91he6?file=src/app/tooltip/tooltip.component.ts)
所以如果想要觸發,我們需要修改傳入新的物件:
(參考程式碼範例: https://stackblitz.com/edit/angular-ivy-c91he6?file=src/app/app.component.ts)
元件或子元件觸發 event handler
如果 OnPush 元件或子元件觸發 event handler,像是按下按鈕,會觸發變更偵測 (元件樹中的所有元件)。
(參考程式碼: https://stackblitz.com/edit/angular-ivy-ai7hob?file=src/app/counter.component.ts)
(注意: global setTimeout 會觸發變更偵測行為,只是子元件在被標記成 onPush 的狀態下,他會在整個變更偵測的情況下被忽略,如果說 markForCheck 才會被視為可以檢查,子元件的數值才會跟著改變。)
注意,在 OnPush 時,下列行為會在觸發變更檢測時,自動被忽略而不會改變畫面:
通過 async pipe 連結到範本上的 Observable 發送一個新的值
(參考程式碼: https://stackblitz.com/edit/angular-asyncpipe-simple-example-bpopnu?file=src/app/app.component.html)
以下為 async pipe update 的原始碼
由上面的 code 我們可以看到,Angular為我們呼叫 markForCheck(),所以我們能看到檢視更新了即使input的參照沒有發生改變。
手動變更偵測的工具
ChangeDetectorRef.detectChanges() :
檢查這個元件跟子元件,可以搭配 detach() 實作本地變更偵測檢查
(未搭配 detach 參考程式碼: https://stackblitz.com/edit/angular-ivy-ekbjro?file=src/app/counter/counter.component.ts)
他只會在當前的 view 和子 view 進行一次變更檢測,也就是說無論當前組件的狀態是什麼,他只會進行一次檢測,如果前面的元件有 trigger detach 把狀態設置違禁用狀態,就有可能不會被檢查。
圖解 ChangeDetectorRef 相關的 Method:
reattach():
將先前分離的視圖重新附加到更改檢測樹。默認情況下,視圖附加到樹。
-> 白話文: 會保留原本被設定成的模式 onPush or Default,在把它重新放到 CD 樹中
detach():
將此視圖與更改檢測樹分離。在重新附加之前,不會檢查分離的視圖。與 detectChanges() 結合使用以實現本地更改檢測檢查。
(搭配 detach 參考程式碼: https://stackblitz.com/edit/ironman2019-change-detector-ref-oupbtv?file=src/app/app.component.ts)
ChangeDetectorRef.markForCheck(): (影響範圍: 自己跟自己之上的元素)
不觸發變更偵測,但將所有 OnPush 標記為檢查一次,不管是在當前或是下一個變更偵測循環中。會對標記的元件執行變更偵測"檢查",本身不觸變更發偵測。
影響範圍:
將自己及 parent Component 等都標示為要被檢查,所以影響範圍是本身以上。
解決 reattach 在沒有啟動檢查的 Parent Component 的 Child Component 沒有作用的問題。
(參考程式碼: https://stackblitz.com/edit/angular-ivy-gyffx7?file=src/app/app.component.ts)
上述標記雖然不是直接觸發變更檢測,但因為標記了需要被檢查,所以當 resource 改變時,一併觸發了變更檢測,然後檢測到需要被檢測的值,就進行了檢測,所以刷新 View。
Reference:
NgModel 的作用為何 ?
Creates a FormControl instance from a domain model and binds it to a form control element.
(引用自: https://angular.io/api/forms/NgModel#see-also)
白話文:
NgModel 創建 FormControl 實例,並且把他綁定到表單控管元件 (form control element)
不同 ngModel 相關的用法
單獨 ngModel + form 的用法
用途:
單獨使用 ngModel 的作用是指 "要向該組件加入一個 property,其 key 會是組件的 name 屬性值,value 為空的字串",這也就是為什麼需要存在 name 的原因。(反之,如果 ngModel 沒有賦值的話,必須要存在 name 的屬性)
使用情境:
提交時,想要讓 input 有初始值,最終提交是使用父表單的值。 (沒有需要針對 Model Value 做操作的情況下使用)
範例程式碼:
(參考程式碼: https://stackblitz.com/edit/angular-ivy-cxz5zk?file=src/app/app.component.ts)
雙向綁定 NgModel + form
使用情境:
提交時,想要將模型 data Model 的值做提交,我們可以使用 NgModel 雙向綁定。
(參考程式碼: https://stackblitz.com/edit/angular-ivy-cxz5zk?file=src/app/app.component.ts)
ngmodel v.s. ng-model 的差別
Stackoverflow 解答:
No, there is no difference, but the one in Angular gives you more flexibility than the one in AngularJS.
[( in Angular is signalling a two-way data binding. Theoretically you could only bind to an event ((ngModel)) or to a value ([ngModel]). This gives you the ability to handle changes going down in a different way than changes coming up. With AngularJS you do not have that flexibility.
(自己查閱的筆記)
屬性指令
如果需要取得自訂元素的指令實體的話,需要記得定義 @ViewChild 以及exportAs 並且設定範本樣式變數才能取得
review(output): https://hsuchihting.github.io/angular/20210304/1004423002/
結構指令
在使用結構指令前,需要先介紹 ViewContainer。
在 Angular 的世界中,View 是一個應用程式 UI 的基本組成。他是最小的 element 組成單位,在同一個 view 中的 element 會同時被新增或同時被摧毀 (destroyed)。Angular 建議開發者把 UI 視為 Views 的組合,而不是 HTML Tag 樹狀結構的一部分。Angular 支援兩種不同類型的 View:
ViewContainerRef 表示 Container 可以 attach 一到多個 view,而任何 DOM 都可以被當作 View Container
回顧到 @Component 需要設定的 attribute 上
可以看到上述的 component 是一個 template 對應到一個 css
而 encapsulation 對應到的預設值是 Emulated
參數說明:
encapsulation: ViewEncapsulation.Emulated
此處的 Emulated 正意味著 Angular 會透過 shadow css 的方式來讓本身組件內容的 css 不會影響到其他樣式,但全域樣式可以影響到現在的樣式
encapsulation: ViewEncapsulation.None
不針對頁面樣式進行封裝,而是將元件樣式放在全域樣式中
encapsulation: ViewEncapsulation.ShadowDom
使用瀏覽器原生的 ShadowDom 機制,元件的檢視與樣式會被放在 Shadow Dom,讓元件樣式不會影響到其他元件,也讓全域性的樣式設定無法影響到此元件樣式