今天要來用 Reactive Forms 的方式實作一個簡單的登入系統,撇開 UI 不談,具體的功能需求規格跟昨天差不多,如下所示:
- 帳號
- 格式為 Email Address,相關規則請參考維基百科,此處則直接使用正規表示法
/^\b[\w\.-]+@[\w\.-]+\.\w{2,4}\b$/gi
來檢驗,驗證有誤時需在欄位後方顯示錯誤訊息:格式有誤,請重新輸入
- 此欄位必填,驗證有誤時需需在欄位後方顯示錯誤訊息:
此欄位必填
- 格式為 Email Address,相關規則請參考維基百科,此處則直接使用正規表示法
- 密碼
- 長度最短不得低於 8 碼,驗證有誤時需需在欄位後方顯示錯誤訊息:
密碼長度最短不得低於8碼
- 長度最長不得超過 16碼,驗證有誤時需需在欄位後方顯示錯誤訊息:
密碼長度最長不得超過16碼
- 此欄位必填,驗證有誤時需需在欄位後方顯示錯誤訊息:
此欄位必填
- 長度最短不得低於 8 碼,驗證有誤時需需在欄位後方顯示錯誤訊息:
- 以上驗證皆需在使用者輸入時動態檢查
- 任一驗證有誤時,登入按鈕皆呈現不可被點選之狀態。
規格需求看清楚之後,我們就來開始實作吧!
實作時大家可以自己開一個專案來練習,抑或是用 Stackblitz 開一個 Angular 的專案來練習,我就不再贅述囉!
如果正在閱讀此篇文章的你還不知道要怎麼開始一個 Angular 專案的話,請先閱讀我的 Angular 深入淺出三十天後再來閱讀此系列文章會比較恰當噢!
實作開始
首先我們先準備好基本的 HTML :
1 | <form> |
未經美化的畫面應該會長這樣:
接著到 app.module.ts
裡 import FormsModule
與 ReactiveFormsModule
:
1 | import { NgModule } from '@angular/core'; |
然後將要綁在 Template 的屬性跟方法都準備好:
1 | export class LoginComponent implements OnInit { |
就可以將這些屬性和方法跟 Template 綁定在一起:
1 | <form [formGroup]="formGroup" (ngSubmit)="login()"> |
到目前為止的程式碼你看懂了多少呢?對於剛接觸 Angular 的表單的朋友來說,今天的資訊量可能會比較大,容我稍微說明一下:
Reactive Forms 的概念是將表單用程式的方式產生。以這個需求來說,這個表單底下會有兩個欄位 account
與 password
,如果將其用 JSON
來表示的話,應該會長這樣:
1 | { |
從資料面來看, {}
代表表單, "account": ""
與 "password": ""
則是裡面的兩個欄位。
而再將其轉換成 Reactive Forms 的概念的話, {}
代表的是 FormGroup
,"account": ""
與 "password": ""
則代表的是 FormControl
。
所以在程式碼中我們可以看到我們宣告 formGroup: FromGroup;
並且在 template 中將其綁定在表單上:
1 | <form [formGroup]="formGroup"> |
並且把表單控制項綁定在對應的 input 欄位上:
1 | <!-- 帳號欄位 --> |
然後在 ngOnInit
裡透過 FormBuilder
來初始化表單:
1 | ngOnInit(): void { |
如此一來,就可以在初始化過後,跟我們的 template 正確綁定了。
而如果當該欄位需要驗證時,就要在初始化時將格式調整成:
1 | ngOnInit(): void { |
如果只有一個要驗證的項目則可以直接放入:
1 | ngOnInit(): void { |
如果有多個要驗證的項目,就用 []
將多個驗證項包起來再放入:
1 | ngOnInit(): void { |
在這裡我們可以發現,上一篇使用 Template Driven Forms 實作時,是用 HTML 原生的屬性來驗證,而今天使用 Reactive Forms 實作時,則是用程式來驗證,如此一來,可以降低表單與 template 之間的依賴性,使得其更易於維護、重用與測試。
Validators 是 Angular 幫我們製作的驗證器,裡面有很多常用驗證器,詳細請參考官方文件
當然我們也可以自己客製驗證器,只要符合 ValidatorFn 的類型即可
關於錯誤訊息基本上可以沿用上一篇的程式,只不過原本是傳入 FormControl
的 errors
來判斷,但現在是傳入整個 FormControl
,為什麼呢?
因為如果只有傳入 FormControl
的 errors
的話,你會發現表單初始化完之後,就會有錯誤訊息顯示在畫面上:
這是因為當我們的表單初始化完之後,驗證器就會開始運作,所以的確那個兩個欄位是有那個錯誤沒錯,但其實這不是我們想要的行為,因為使用者根本就還沒有開始填表單,我們想要的是當使用者開始填表單之後,才會顯示對應的錯誤訊息,所以我們改傳入整個 FormControl
,它其中有幾個很好用的屬性可以使用:
pristine
─ 如果此屬性為true
,代表該欄位是乾淨,沒有被輸入過值;反之則代表有被輸入過值,與dirty
成反比。touched
─ 如果此屬性為true
,代表該欄位曾經被碰(該欄位曾經被使用滑鼠 focus 過);反之則代表該欄位完全沒被碰過。dirty
─ 如果此屬性為true
,代表該欄位曾經被輸入過值,已經髒掉了;反之則代表該欄位是乾淨,沒有被輸入過值,與pristine
成反比。
想知道更多可以參考官方文件: FormControl 與其抽象類別 AbstractControl
所以我們只要加上當該欄位是乾淨的,就不回傳錯誤訊息的判斷就可以了,像是這樣:
1 | getErrorMessage(formControl: FormControl): string { |
最終結果:
本日小結
對於第一次接觸 Reactive Forms 的朋友們,今天的資訊量會比較多,但重點大致上可歸納成以下四點:
- 學習如何將表單用程式的方式寫出來,心法:「資料即表單,表單即資料」。
- 學習如何使用表單物件 FormBuilder 、 FormGroup 與 FormControl 。
- 學習如何使用 Validators 來驗證使用者所輸入的值。
- 學習如何將表單物件與 Template 綁定。
此外,千萬記得要 import FormsModule
與 ReactiveFormsModule
才可以使用噢!
我一樣會將今日的實作程式碼放在 Stackblitz 上供大家參考,建議大家在看我的實作之前,先按照需求規格自己做一遍,之後再跟我的對照,看看自己的實作跟我的實作不同的地方在哪裡、有什麼好處與壞處,如此反覆咀嚼消化後,我相信你一定可以進步地非常快!
如果有任何的問題或是回饋,也都非常歡迎留言給我讓我知道噢!