角度表單驗證
簡介
任何 Web 應用程序中最常見的功能之一是為用戶提供一個表單來輸入一些數據。您每天都使用表單來登錄、註冊、下訂單等。
在驗證之前處理用戶輸入可能會產生嚴重後果。您最終可能會存儲無效數據,例如不正確的日期、電子郵件、年齡等。由於跨站點腳本 (XSS) 等攻擊,這也可能是一個安全問題。
驗證 HTML 表單的傳統方法是使用 JavaScript 或 JQuery。不幸的是,這種方法需要一堆代碼。
Angular 作為一個成熟的框架,為驗證用戶輸入和顯示驗證消息提供了出色的支持。它有很多常用的內置驗證器,您可以利用它們,甚至可以編寫自定義驗證器。
Angular 中的表單
Angular 表單是一種常規的 HTML 表單,幾乎沒有附加功能。對於表單中的每個字段(輸入、單選、選擇等),我們需要一個 FormControl
的對象 班級。 FormControl
對象提供有關該字段的信息。它的 value
, 如果值為 valid
,如果它是無效的驗證 errors
等。
它還提供了字段的狀態,例如 touched
, untouched
, dirty
, pristine
等。
同樣,一個 FormGroup
是 FormControl
的集合 對象。每個 Angular 表單至少有一個 FormGroup
.您可能決定擁有多個 FormGroup
s 在用例中,例如分離用戶註冊表單的個人詳細信息和專業詳細信息部分的處理。
FormGroup
的所有屬性 (valid
, error
等)也可用於 FormControl
.例如,valid
FormControl
的屬性 將返回 true
如果所有 FormControl
實例是有效的。
因此,要向 Angular 表單添加驗證,我們需要做兩件事:
- 至少一個
FormGroup
表單對象 - 一個
FormControl
表單中每個字段的對象
有兩種不同的方式 這些控制對象 可以創建。我們可以提供一些指令 在模板中 表單和 Angular 可以創建這樣的 控件 在我們的引擎蓋下。通過這種方式創建的表單稱為模板驅動表單 .
如果我們有一些特殊的用例,並且我們想要更多地控製表單,我們可以顯式地創建這樣的 控制對象 .以這種方式創建的表單稱為反應式表單 .
模板驅動表單
在模板驅動的表單中,我們應用 ngModel
模板中每個字段的指令。 Angular 創建一個 FormControl
每個此類字段的引擎蓋下的對象並將其與相應的字段相關聯:
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control" id="name"
ngModel name="name">
</div>
<div class="form-group">
<label for="username">Username</label>
<input type="text" class="form-control" id="username"
ngModel name="username">
</div>
注意 :使用 ngModel
, 需要提供 name
屬性或定義 FormControl
作為 ngModelOptions
中的“獨立” , 否則 Angular 會拋出錯誤。
另外,在 app.module.ts
你需要添加 FormsModule
導入數組:
import { FormsModule } from '@angular/forms';
// ...some other imports
imports: [
//...some other imports
FormsModule
]
模板驅動表單中的驗證
Angular 提供了一些內置驗證器 驗證常見用例。為了使用內置驗證器,您需要將驗證屬性應用於您想要進行驗證的每個表單字段。這些驗證屬性與 required
等常規 HTML5 驗證屬性相同 , minlength
, maxlength
等等。在底層,Angular 提供了指令來將這些屬性與 Angular 框架中定義的驗證器函數相匹配。
每當 FormControl
的值發生變化時,Angular 通過運行驗證生成驗證錯誤列表。如果列表為空,則為有效狀態,否則為無效狀態。
假設我們要在其中加入以下驗證:
- 作為字段名稱 和用戶名 有
required
屬性,如果該字段為空,我們希望顯示驗證消息。 - 名字 字段應該有一個
minlegth
的值 和maxlength
應分別為 2 和 30 個字符。 - 如果用戶名有空格,則顯示無效的用戶名消息。
對於我們要添加驗證的每個表單控件,我們需要添加適當的驗證屬性並導出 ngModel
到本地模板變量 :
<input type="text" class="form-control" id="name"
required maxlength="30" minlength="2"
ngModel name="name" #name="ngModel">
在上面的示例中,我們使用了以下內置驗證器 - required
, minlength
, 和 maxlength
.
我們可以使用模板變量name
在模板中檢查所用驗證器的驗證狀態:
<div *ngIf="name.invalid && (name.dirty || name.touched)"
class="alert alert-danger">
<div *ngIf="name.errors.required">
Name is required.
</div>
<div *ngIf="name.errors.minlength">
Name cannot be more than 30 characters long.
</div>
<div *ngIf="name.errors.minlength">
Name must be at least 2 characters long.
</div>
</div>
因為我們使用條件語句來呈現第一個 div
, 只有當內置驗證器的狀態為 invalid
時才會顯示 .我們已經在本節的開頭解釋瞭如何將狀態確定為 valid
或 invalid
.
同樣,內部 div's
僅當模板變量 name
時才會顯示 有一個屬性 errors
和 errors
屬性具有以下屬性之一 - required
, minlength
和 maxlength
和屬性值 id true
.我們已經討論過模板變量如何綁定到 ngModel
指令,並且每當表單控件發生任何變化以及 Angular 對該字段運行驗證後,它都會接收這些屬性。
注意 :檢查 dirty
很重要 和 touched
狀態,否則將在頁面第一次加載時顯示錯誤消息,這對用戶體驗不利。我們需要驗證消息在以下情況之一顯示:
- 用戶更改了一些值,即該字段是臟的(
formControlObject.dirty
) - 用戶使用選項卡或單擊將焦點切換到其他元素,即該字段被觸摸(
formControlObject.touched
)
如果你想參考 Angular 內置驗證器的完整列表,你可以關注 Validators API。
編寫自定義驗證器
有時,內置驗證器可能無法涵蓋您的確切用例。在這種情況下,您可能需要創建自定義驗證器函數。
驗證器函數實現 ValidatorFn
接口,這意味著它應該有簽名:
interface ValidatorFn {
(control: AbstractControl): ValidationErrors | null
}
ValidationErrors
應該是具有一個或多個鍵值對的對象:
免費電子書:Git Essentials
查看我們的 Git 學習實踐指南,其中包含最佳實踐、行業認可的標準以及隨附的備忘單。停止谷歌搜索 Git 命令並真正學習 它!
type ValidationErrors = {
[key: string]: any;
};
鍵應該是一個字符串,用於表示驗證錯誤的類型,如 invalidEmail
, required
等。該值可以是任何值,用於提供有關驗證錯誤的更多信息。
對於上面的例子,我們想編寫一個自定義驗證函數來驗證 username 中是否沒有空格 .
雖然從技術上講,我們可以在應用程序的任何地方編寫此函數,但將所有相關的驗證器函數放在一個單獨的類中始終是一種好習慣:
import { ValidationErrors, AbstractControl } from '@angular/forms';
export class UserRegistrationFormValidators {
static usernameShouldBeValid(control: AbstractControl): ValidationErrors | null {
if ((control.value as string).indexOf(' ') >= 0) {
return { shouldNotHaveSpaces: true }
}
// If there is no validation failure, return null
return null;
}
}
注意 :在這個例子中,我們返回了 true
作為鍵 shouldNotHaveSpaces
的值 因為我們不需要提供任何細節。在某些情況下,您可能需要提供詳細信息,例如:
return { maxlengthExceeded: {
maxLength: 20,
actual: control.value.length
}
}
接下來,我們可以使用這個驗證器函數UserRegistrationFormValidators.usernameShouldBeValid
對於 username
模板驅動表單中的表單控件:
<div class="form-group">
<label for="username">Username</label>
<input type="text" class="form-control" id="username"
required
UserRegistrationFormValidators.usernameShouldBeValid
[(ngModel)]="person.username" name="username">
</div>
反應式表單
在響應式表單中,我們創建 FormControl
組件中明確的對象 那個模板。這是沒有任何 ngModel
的常規 HTML 表單 指令或驗證:
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control" id="name">
</div>
<div class="form-group">
<label for="username">Username</label>
<input type="text" class="form-control" id="username">
</div>
讓我們假設我們想要將前面示例中的模板驅動表單轉換為響應式表單。
為此,首先,我們需要顯式創建 FormGroup
和 FormControls
組件中的每個字段 模板:
form = new FormGroup({
'name': new FormControl(),
'username': new FormControl(),
})
注意 :如前所述,一個表單可以有多個 FormGroup
.在這種情況下,我們可以有一個嵌套結構:
registrationForm = new FormGroup({
'personalDetailsForm': new FormGroup({
'name': new FormControl()
})
})
你可以閱讀更多關於 FormGroup
在 Angular 文檔中。
讓我把你的注意力帶回到我們的用例上。
接下來,我們需要關聯這些FormControl
對象添加到 HTML 表單中的字段。
<form [formGroup]="registrationForm">
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control" id="name"
[formControlName]="name">
</div>
<div class="form-group">
<label for="username">Username</label>
<input type="text" class="form-control" id="username"
[formControlName]="username">
</div>
<form>
這裡我們應用了 formGroup
指令並將其與 FormGroup
關聯 對象 registrationForm
我們在 component 中創建的 .我們還關聯了 formControlName
帶有相應 FormControl
的指令 對象 name
和 username
.
注意 :構建響應式表單的指令 在 ReactiveFormsModule
中定義 .所以如果你得到一個錯誤,比如:
Can't bind to formGroup
...那麼您應該檢查您是否已導入該 ReactiveFormsModule
在你的主模塊 app.module.ts
.
反應形式的驗證
在反應形式中,我們不傳遞 ngModel
指令,我們也不使用 HTML5 驗證屬性。我們在創建 FormControl
的對象時指定驗證器 在組件本身中。
這是 FormControl
的簽名 類:
class FormControl extends AbstractControl {
constructor(formState: any = null, validatorOrOpts?: ValidatorFn | AbstractControlOptions | ValidatorFn[], asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[])
// ...
}
我們可以看到第一個參數是控件的初始狀態,可以保持為空,即 ''
.第二個參數是ValidatorFn
.
為 FormControl
添加內置驗證器函數 我們可以傳遞給它適當的 ValidatorFn
.對於以下示例,我們使用了以下內置驗證器 required
, minLength
, 和 maxLength
- :
registrationForm = new FormGroup({
'name': new FormControl('Enter your name', [
Validators.required,
Validators.minLength(2),
Validators.maxLength(30)
]),
'username': new FormControl('', Validators.required),
})
注意 :您需要導入 Validators
在組件中。
另請注意,與模板驅動的表單不同,我們不使用驗證屬性 .我們使用各自的 ValidatorFn
像 Validators.required
, Validators.minLength(2) 等。您的代碼編輯器可能會為所有 ValidatorFn
提供自動完成功能 輸入 Validators
的那一刻 後跟一個點 .
.
我們可以回到模板 並編寫驗證消息:
<form [formGroup]="registrationForm">
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control" id="name"
[formControlName]="name">
<div *ngIf="registrationForm.get('name').invalid && (registrationForm.get('name').dirty || registrationForm.get('name').touched)"
class="alert alert-danger">
<div *ngIf="registrationForm.get('name').errors.required">
Name is required.
</div>
<div *ngIf="registrationForm.get('name').errors.minlength">
Name cannot be more than 30 characters long.
</div>
<div *ngIf="registrationForm.get('name').errors.minlength">
Name must be at least 2 characters long.
</div>
</div>
</div>
<div class="form-group">
<label for="username">Username</label>
<input type="text" class="form-control" id="username"
[formControlName]="username">
</div>
<form>
響應式表單的自定義驗證器
我們需要編寫自定義驗證器函數 與我們為 模板驅動 所做的相同 表格部分。我們可以使用相同的自定義驗證器函數 UserRegistrationFormValidators.usernameShouldBeValid
在 響應式表單的組件中 :
registrationForm = new FormGroup({
'name': new FormControl('Enter your name', [
Validators.required,
Validators.minLength(2),
Validators.maxLength(30)
]),
'username': new FormControl('', [
Validators.required,
UserRegistrationFormValidators.usernameShouldBeValid
]),
})
結論
在本教程中,我們探討了處理用戶輸入的兩種不同方式——模板驅動和反應式表單。我們學習瞭如何對兩種類型的表單進行驗證。最後,我們還編寫了自定義驗證器函數並將其包含在內置驗證器中。
正如我們所見,Angular 對錶單有很好的支持,並提供了一些底層有用的特性來驗證表單。使用 Angular 表單提供每一個功能超出了本教程的範圍。您可以閱讀 Angular 文檔以獲取完整信息。