今天要來用 Template Driven Forms 的方式實作一個簡單的登入系統,撇開 UI 不談,具體的功能需求規格如下:
/^\b[\w\.-]+@[\w\.-]+\.\w{2,4}\b$/gi
來檢驗,驗證有誤時需在欄位後方顯示錯誤訊息:格式有誤,請重新輸入
此欄位必填
密碼長度最短不得低於8碼
此欄位必填
規格需求看清楚之後,我們就來開始實作吧!
實作時大家可以自己開一個專案來練習,抑或是用 Stackblitz 開一個 Angular 的專案來練習,我就不再贅述囉!
如果正在閱讀此篇文章的你還不知道要怎麼開始一個 Angular 專案的話,請先閱讀我的 Angular 深入淺出三十天後再來閱讀此系列文章會比較恰當噢!
首先我們先準備好基本的 HTML :
<form>
<p>
<label for="account">帳號:</label>
<input type="email" name="account" id="account">
</p>
<p>
<label for="password">密碼:</label>
<input type="password" name="password" id="password">
</p>
<p>
<button type="submit">登入</button>
</p>
</form>
未經美化的畫面應該會長這樣:
接著到 app.module.ts
裡 import FormsModule
:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
@NgModule({
imports: [
BrowserModule,
FormsModule
],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule { }
然後將要綁在 Template 的屬性跟方法都準備好:
export class AppComponent {
// 綁定在帳號欄位上
account = '';
// 綁定在密碼欄位上
password = '';
// 帳號欄位的錯誤訊息
accountErrorMessage = '';
// 密碼欄位的錯誤訊息
passwordErrorMessage = '';
/**
* 綁定在帳號欄位上,當使用者改變登入帳號時,會觸發此函式,並取得對應的錯誤訊息
*
* @param {string} account
* @param {ValidationErrors} errors
*/
accountValueChange(account: string, errors: ValidationErrors): void {
this.account = account;
this.validationCheck(errors, 'account');
}
/**
* 綁定在密碼欄位上,當使用者改變密碼時會觸發此函式
*
* @param {string} password
* @param {ValidationErrors} errors
*/
passwordValueChange(password: string, errors: ValidationErrors): void {
this.password = password;
this.validationCheck(errors, 'password');
}
// 綁定在表單上,當使用者按下登入按鈕時會觸發此函式
login(): void {
// do login...
}
/**
* 透過欄位裡的 ValidationErrors 來設定該欄位的錯誤訊息
*
* @param {ValidationErrors} errors 欲驗證的欄位的錯誤 (by Angular)
* @param {'account' | 'password'} fieldName 欄位名稱
*/
private validationCheck(
errors: ValidationErrors,
fieldName: 'account' | 'password'
): void {
let errorMessage: string;
if (!errors) {
errorMessage = '';
} else if (errors.required) {
errorMessage = '此欄位必填';
} else if (errors.pattern) {
errorMessage = '格式有誤,請重新輸入';
} else if (errors.minlength) {
errorMessage = '密碼長度最短不得低於8碼';
}
this.setErrorMessage(fieldName, errorMessage);
}
/**
* 設定指定欄位的錯誤訊息
*
* @param {'account' | 'password'} fieldName 欲設定錯誤訊息的欄位名稱
* @param {string} errorMessage 欲設定的錯誤訊息
*/
private setErrorMessage(
fieldName: 'account' | 'password',
errorMessage: string
): void {
if (fieldName === 'account') {
this.accountErrorMessage = errorMessage;
} else {
this.passwordErrorMessage = errorMessage;
}
}
}
就可以將這些屬性和方法跟 Template 綁定在一起:
<form #form="ngForm" (ngSubmit)="login()">
<p>
<label for="account">帳號:</label>
<input
type="email"
name="account"
id="account"
required
pattern="\b[\w\.-]+@[\w\.-]+\.\w{2,4}\b"
#accountNgModel="ngModel"
[ngModel]="account"
(ngModelChange)="accountValueChange(accountNgModel.value, accountNgModel.errors)"
/>
<span class="error-message">{{ accountErrorMessage }}</span>
</p>
<p>
<label for="password">密碼:</label>
<input
type="password"
name="password"
id="password"
required
#passwordNgModel="ngModel"
[minlength]="8"
[maxlength]="16"
[ngModel]="password"
(ngModelChange)="passwordValueChange(passwordNgModel.value, passwordNgModel.errors)"
/>
<span class="error-message">{{ passwordErrorMessage }}</span>
</p>
<p>
<button type="submit" [disabled]="form.invalid">登入</button>
</p>
</form>
到目前為止的程式碼你看懂了多少呢?容我稍微說明一下:
首先是關於必填檢核,只要 <input ...>
欄位裡加上 HTML 原生的屬性 ─ required
即可。
帳號欄位的格式檢查則是使用原生的屬性 ─ pattern
,這個屬性可以直接使用正規表示法的方式來檢查使用者所輸入的值是否符合我們所訂定的格式。不過要注意的是,頭尾不需要特別加上 /^$/
,所以整串表示法只需要用到中間的部份 ─ \b[\w\.-]+@[\w\.-]+\.\w{2,4}\b
。
對這個屬性較不熟悉的朋友可以參照 MDN 的說明文件。
字數長度的檢核也是使用原生的屬性 ─ minlength
與 maxlength
。這部份有兩個地方需要特別留意:
maxlength
是會阻擋使用者輸入的,當需求是要檢核長度但不能阻擋使用者輸入的話,就不能使用這個方式。很多人剛學會用 Angular 的朋友,在使用 ngModel
時都會忘記這兩件事情:
FormsModule
input
要有 name
屬性使用範本語法 #accountNgModel="ngModel"
、 #passwordNgModel="ngModel"
來取得型別為 NgModel 的物件,因為我們可以從中取得該欄位的 value
與 errors
,前者指定給其相關屬性,後者用以判斷該欄位的錯誤,以設定相對應的錯誤訊息。
單純使用
#accountNgModel
與#accountNgModel="ngModel"
的差別在於前者取得的是單純的 HTMLInputElement 物件。
#form="ngForm"
來取得型別為 NgForm 的表單物件。單純使用
#form
與#form="ngForm"
的差別在於前者取得的是單純的 HTMLFormElement 物件。
[disabled]="form.invalid"
的綁定,讓按鈕在表單無效時,無法按下登入按鈕。至此,我們就完成今天的目標囉!是不是很簡單呢?!
最後的結果應該要像這樣:
剛開始學習 Angular 的朋友,通常都會因為不熟悉 Angular 的語法而導致明明很簡單的功能卻要弄得很複雜。
今天的學習重點主要有以下三點:
#accountNgModel="ngModel"
與 #form="ngForm"
。ValidationErrors
取得相應的錯誤訊息。我會將今日的實作程式碼放在 Stackblitz 上供大家參考,建議大家在看我的實作之前,先按照需求規格自己做一遍,之後再跟我的對照,看看自己的實作跟我的實作不同的地方在哪裡、有什麼好處與壞處,如此反覆咀嚼消化後,我相信你一定可以進步地非常快!
如果有任何的問題或是回饋,也都非常歡迎留言給我讓我知道噢!
2021-09-19 22:54:50
,感謝熱心讀者「程凱大」指正錯誤,已移除所有的 FormControl
,原因是因為在 Template Driven Forms 的範圍裡, NgModel 本身已有我們所需之屬性,是我自己豬頭捨近求遠,再次衷心感謝。平常都是用 Reactive Forms,在這裡看到 Template Driven Forms 的實作,學到新東西了!
Hi TD
很高興有幫到你,其實我個人平常也很少在用,但是對於很多初學者來說,我覺得這是一個必經之路。