Angular 4.x中表單Reactive Forms詳解
Angular 4.x 中有兩種表單:
- Template-Driven Forms - 模板驅(qū)動(dòng)式表單 (類似于 Angular 1.x 中的表單 )
- Reactive Forms (Model-Driven Forms) - 響應(yīng)式表單
Template-Driven Forms (模板驅(qū)動(dòng)表單) ,我們之前的文章已經(jīng)介紹過(guò)了,了解詳細(xì)信息,請(qǐng)查看 - Angular 4.x Template-Driven Forms 。
Contents
- ngModule and reactive forms
- FormControl and FormGroup
- Implementing our FormGroup model
- Binding our FormGroup model
- Reactive submit
- Reactive error validation
- Simplifying with FormBuilder
Form base and interface
Form base
<form novalidate> <label> <span>Full name</span> <input type="text" name="name" placeholder="Your full name"> </label> <div> <label> <span>Email address</span> <input type="email" name="email" placeholder="Your email address"> </label> <label> <span>Confirm address</span> <input type="email" name="confirm" placeholder="Confirm your email address"> </label> </div> <button type="submit">Sign up</button> </form>
接下來(lái)我們要實(shí)現(xiàn)的功能如下:
- 綁定 name、email、confirm 輸入框的值
- 為所有輸入框添加表單驗(yàn)證功能
- 顯示驗(yàn)證異常信息
- 表單驗(yàn)證失敗時(shí),不允許進(jìn)行表單提交
- 表單提交功能
User interface
// signup.interface.ts
export interface User {
name: string;
account: {
email: string;
confirm: string;
}
}
ngModule and reactive forms
在我們繼續(xù)深入介紹 reactive forms 表單前,我們必須在 @NgModule 中導(dǎo)入 @angular/forms 庫(kù)中的 ReactiveFormsModule:
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
imports: [
...,
ReactiveFormsModule
],
declarations: [...],
bootstrap: [...]
})
export class AppModule {}
友情提示:若使用 reactive forms,則導(dǎo)入 ReactiveFormsModule;若使用 template-driven 表單,則導(dǎo)入 FormsModule。
Reactive approach
我們將基于上面的定義的基礎(chǔ)表單,創(chuàng)建 SignupFormComponent :
signup-form.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'signup-form',
template: `
<form novalidate>...</form>
`
})
export class SignupFormComponent {
constructor() {}
}
這是一個(gè)基礎(chǔ)的組件,在我們實(shí)現(xiàn)上述功能前,我們需要先介紹 FormControl、FormGroup、FormBuilder 的概念和使用。
FormControl and FormGroup
我們先來(lái)介紹一下 FormControl 和 FormGroup 的概念:
1、FormControl - 它是一個(gè)為單個(gè)表單控件提供支持的類,可用于跟蹤控件的值和驗(yàn)證狀態(tài),此外還提供了一系列公共API。
使用示例:
ngOnInit() {
this.myControl = new FormControl('Semlinker');
}
2、FormGroup - 包含是一組 FormControl 實(shí)例,可用于跟蹤 FormControl 組的值和驗(yàn)證狀態(tài),此外也提供了一系列公共API。
使用示例:
ngOnInit() {
this.myGroup = new FormGroup({
name: new FormControl('Semlinker'),
location: new FormControl('China, CN')
});
}
現(xiàn)在我們已經(jīng)創(chuàng)建了 FormControl 和 FormGroup 實(shí)例,接下來(lái)我們來(lái)看一下如何使用:
<form novalidate [formGroup]="myGroup"> Name: <input type="text" formControlName="name"> Location: <input type="text" formControlName="location"> </form>
注意事項(xiàng):Template-Driven Forms 中介紹的 ngModel 和 name="" 屬性,已經(jīng)被移除了。這是一件好事,讓我們的模板更簡(jiǎn)潔。
上面示例中,我們必須使用 [formGroup] 綁定我們創(chuàng)建的 myGroup 對(duì)象,除此之外還要使用 formControlName 指令,綁定我們創(chuàng)建的 FormControl 控件。
此時(shí)的表單結(jié)構(gòu)如下:
FormGroup -> 'myGroup' FormControl -> 'name' FormControl -> 'location'
Implementing our FormGroup model
signup.interface.ts
export interface User {
name: string;
account: {
email: string;
confirm: string;
}
}
與之對(duì)應(yīng)的表單結(jié)構(gòu)如下:
FormGroup -> 'user' FormControl -> 'name' FormGroup -> 'account' FormControl -> 'email' FormControl -> 'confirm'
是的,我們可以創(chuàng)建嵌套的 FormGroup 集合!讓我們更新一下組件 (不包含初始數(shù)據(jù)):
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
@Component({...})
export class SignupFormComponent implements OnInit {
user: FormGroup;
ngOnInit() {
this.user = new FormGroup({
name: new FormControl(''),
account: new FormGroup({
email: new FormControl(''),
confirm: new FormControl('')
})
});
}
}
如果我們想要設(shè)置初始數(shù)據(jù),我們可以按照上述示例進(jìn)行設(shè)置。通常情況下,我們通過(guò)服務(wù)端提供的 API 接口來(lái)獲取表單的初始信息。
Binding our FormGroup model
現(xiàn)在我們已經(jīng)實(shí)例化了 FormGroup 模型,是時(shí)候綁定到對(duì)應(yīng)的 DOM 元素上了。具體示例如下:
<form novalidate [formGroup]="user"> <label> <span>Full name</span> <input type="text" placeholder="Your full name" formControlName="name"> </label> <div formGroupName="account"> <label> <span>Email address</span> <input type="email" placeholder="Your email address" formControlName="email"> </label> <label> <span>Confirm address</span> <input type="email" placeholder="Confirm your email address" formControlName="confirm"> </label> </div> <button type="submit">Sign up</button> </form>
現(xiàn)在 FormGroup 與 FormControl 對(duì)象與 DOM 結(jié)構(gòu)的關(guān)聯(lián)信息如下:
// JavaScript APIs FormGroup -> 'user' FormControl -> 'name' FormGroup -> 'account' FormControl -> 'email' FormControl -> 'confirm' // DOM bindings formGroup -> 'user' formControlName -> 'name' formGroupName -> 'account' formControlName -> 'email' formControlName -> 'confirm'
當(dāng)使用模板驅(qū)動(dòng)的表單時(shí),為了獲取 f.value 表單的值,我們需要先執(zhí)行 #f="ngForm" 的操作。而對(duì)于使用響應(yīng)式的表單,我們可以通過(guò)以下方式,方便的獲取表單的值:
{{ user.value | json }} // { name: '', account: { email: '', confirm: '' }}
Reactive submit
跟模板驅(qū)動(dòng)的表單一樣,我們可以通過(guò) ngSubmit 輸出屬性,處理表單的提交邏輯:
<form novalidate (ngSubmit)="onSubmit(user)" [formGroup]="user"> ... </form>
需要注意的是:我們使用 user 對(duì)象作為 onSubmit() 方法的參數(shù),這使得我們可以獲取表單對(duì)象的相關(guān)信息,具體處理邏輯如下:
export class SignupFormComponent {
user: FormGroup;
onSubmit({ value, valid }: { value: User, valid: boolean }) {
console.log(value, valid);
}
}
上面代碼中,我們使用 Object destructuring (對(duì)象解構(gòu)) 的方式,從user 對(duì)象中獲取 value 和 valid 屬性的值。其中 value 的值,就是 user.value 的值。在實(shí)際應(yīng)用中,我們是不需要傳遞 user 參數(shù)的:
export class SignupFormComponent {
user: FormGroup;
onSubmit() {
console.log(this.user.value, this.user.valid);
}
}
表單的數(shù)據(jù)綁定方式和提交邏輯已經(jīng)介紹完了,是該介紹表單實(shí)際應(yīng)用中,一個(gè)重要的環(huán)節(jié) — 表單驗(yàn)證。
Reactive error validation
接下來(lái)我們來(lái)為表單添加驗(yàn)證規(guī)則,首先我們需要從 @angular/forms 中導(dǎo)入 Validators。具體使用示例如下:
ngOnInit() {
this.user = new FormGroup({
name: new FormControl('', [Validators.required, Validators.minLength(2)]),
account: new FormGroup({
email: new FormControl('', Validators.required),
confirm: new FormControl('', Validators.required)
})
});
}
通過(guò)以上示例,我們可以看出,如果表單控制包含多種驗(yàn)證規(guī)則,可以使用數(shù)組聲明多種驗(yàn)證規(guī)則。若只包含一種驗(yàn)證規(guī)則,直接聲明就好。通過(guò)這種方式,我們就不需要在模板的輸入控件中添加 required 屬性。接下來(lái)我們來(lái)添加表單驗(yàn)證失敗時(shí),不允許進(jìn)行表單提交功能:
<form novalidate (ngSubmit)="onSubmit(user)" [formGroup]="user"> ... <button type="submit" [disabled]="user.invalid">Sign up</button> </form>
那么問(wèn)題來(lái)了,我們要如何獲取表單控件的驗(yàn)證信息?我們可以使用模板驅(qū)動(dòng)表單中介紹的方式,具體如下:
<form novalidate [formGroup]="user">
{{ user.controls.name?.errors | json }}
</form>
友情提示: ?.prop 稱為安全導(dǎo)航操作符,用于告訴 Angular prop 的值可能不存在。
此外我們也可以使用 FormGroup 對(duì)象提供的 API,來(lái)獲取表單控件驗(yàn)證的錯(cuò)誤信息:
<form novalidate [formGroup]="user">
{{ user.get('name').errors | json }}
</form>
現(xiàn)在我們來(lái)看一下完整的代碼:
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { User } from './signup.interface';
@Component({
selector: 'signup-form',
template: `
<form novalidate (ngSubmit)="onSubmit(user)" [formGroup]="user">
<label>
<span>Full name</span>
<input type="text" placeholder="Your full name" formControlName="name">
</label>
<div class="error" *ngIf="user.get('name').hasError('required') &&
user.get('name').touched">
Name is required
</div>
<div class="error" *ngIf="user.get('name').hasError('minlength') &&
user.get('name').touched">
Minimum of 2 characters
</div>
<div formGroupName="account">
<label>
<span>Email address</span>
<input type="email" placeholder="Your email address" formControlName="email">
</label>
<div
class="error"
*ngIf="user.get('account').get('email').hasError('required') &&
user.get('account').get('email').touched">
Email is required
</div>
<label>
<span>Confirm address</span>
<input type="email" placeholder="Confirm your email address"
formControlName="confirm">
</label>
<div
class="error"
*ngIf="user.get('account').get('confirm').hasError('required') &&
user.get('account').get('confirm').touched">
Confirming email is required
</div>
</div>
<button type="submit" [disabled]="user.invalid">Sign up</button>
</form>
`
})
export class SignupFormComponent implements OnInit {
user: FormGroup;
constructor() {}
ngOnInit() {
this.user = new FormGroup({
name: new FormControl('', [Validators.required, Validators.minLength(2)]),
account: new FormGroup({
email: new FormControl('', Validators.required),
confirm: new FormControl('', Validators.required)
})
});
}
onSubmit({ value, valid }: { value: User, valid: boolean }) {
console.log(value, valid);
}
}
功能是實(shí)現(xiàn)了,但創(chuàng)建 FormGroup 對(duì)象的方式有點(diǎn)繁瑣,Angular 團(tuán)隊(duì)也意識(shí)到這點(diǎn),因此為我們提供 FormBuilder ,來(lái)簡(jiǎn)化上面的操作。
Simplifying with FormBuilder
首先我們需要從 @angular/forms 中導(dǎo)入 FormBuilder:
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
export class SignupFormComponent implements OnInit {
user: FormGroup;
constructor(private fb: FormBuilder) {}
...
}
然后我們使用 FormBuilder 對(duì)象提供的 group() 方法,來(lái)創(chuàng)建 FormGroup 和 FormControl 對(duì)象:
調(diào)整前的代碼 (未使用FormBuilder):
ngOnInit() {
this.user = new FormGroup({
name: new FormControl('', [Validators.required, Validators.minLength(2)]),
account: new FormGroup({
email: new FormControl('', Validators.required),
confirm: new FormControl('', Validators.required)
})
});
}
調(diào)整后的代碼 (使用FormBuilder):
ngOnInit() {
this.user = this.fb.group({
name: ['', [Validators.required, Validators.minLength(2)]],
account: this.fb.group({
email: ['', Validators.required],
confirm: ['', Validators.required]
})
});
}
對(duì)比一下調(diào)整前和調(diào)整后的代碼,是不是感覺(jué)一下子方便了許多。此時(shí)更新完后完整的代碼如下:
@Component({...})
export class SignupFormComponent implements OnInit {
user: FormGroup;
constructor(private fb: FormBuilder) {}
ngOnInit() {
this.user = this.fb.group({
name: ['', [Validators.required, Validators.minLength(2)]],
account: this.fb.group({
email: ['', Validators.required],
confirm: ['', Validators.required]
})
});
}
onSubmit({ value, valid }: { value: User, valid: boolean }) {
console.log(value, valid);
}
}
我有話說(shuō)
Template-Driven Forms vs Reactive Forms
Template-Driven Forms (模板驅(qū)動(dòng)表單) 的特點(diǎn)
- 使用方便
- 適用于簡(jiǎn)單的場(chǎng)景
- 通過(guò) [(ngModel)] 實(shí)現(xiàn)數(shù)據(jù)雙向綁定
- 最小化組件類的代碼
- 不易于單元測(cè)試
Reactive Forms (響應(yīng)式表單) 的特點(diǎn)
- 比較靈活
- 適用于復(fù)雜的場(chǎng)景
- 簡(jiǎn)化了HTML模板的代碼,把驗(yàn)證邏輯抽離到組件類中
- 方便的跟蹤表單控件值的變化
- 易于單元測(cè)試
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
- React中使用async validator進(jìn)行表單驗(yàn)證的實(shí)例代碼
- ReactJS實(shí)現(xiàn)表單的單選多選和反選的示例
- 詳解Angular Reactive Form 表單驗(yàn)證
- 基于React實(shí)現(xiàn)表單數(shù)據(jù)的添加和刪除詳解
- 通過(guò)實(shí)例學(xué)習(xí)React中事件節(jié)流防抖
- React中阻止事件冒泡的問(wèn)題詳析
- 詳解react關(guān)于事件綁定this的四種方式
- React.Js添加與刪除onScroll事件的方法詳解
- 在React中如何優(yōu)雅的處理事件響應(yīng)詳解
- 詳解Python的Twisted框架中reactor事件管理器的用法
- 詳細(xì)分析React 表單與事件
相關(guān)文章
每個(gè)Angular版本在其生命周期中都經(jīng)歷了各個(gè)階段。組件在Angular中起著關(guān)鍵作用; 在這里,本文將討論Angular中的組件生命周期以及它們?nèi)绾斡绊懣蚣芩邪姹镜纳芷凇?/div> 2021-05-05
AngularJS學(xué)習(xí)筆記之ng-options指令
ng-options是angular-1.3新出的一個(gè)指令,這篇文章就來(lái)介紹這個(gè)指令的用法.有需要的小伙伴可以參考下。2015-06-06
angular6.0開(kāi)發(fā)教程之如何安裝angular6.0框架
這篇文章主要介紹了angular6.0開(kāi)發(fā)教程之如何安裝angular6.0框架,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-06-06
angular使用post、get向后臺(tái)傳參的問(wèn)題實(shí)例
本篇文章主要介紹了angular使用post、get向后臺(tái)傳參的問(wèn)題實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-05-05
Angular實(shí)現(xiàn)一個(gè)簡(jiǎn)單的多選復(fù)選框的彈出框指令實(shí)例
下面小編就為大家?guī)?lái)一篇Angular實(shí)現(xiàn)一個(gè)簡(jiǎn)單的多選復(fù)選框的彈出框指令實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-04-04
JavaScript框架Angular和React深度對(duì)比
這篇文章主要介紹了JS框架中Angular和React不同和差異,以及兩個(gè)框架的深度對(duì)比介紹。2017-11-11最新評(píng)論

