angular提供了模板驅(qū)動(dòng)和模型驅(qū)動(dòng)兩種方式來(lái)構(gòu)建表單。模板驅(qū)動(dòng)模式使用模板表單內(nèi)置指令、內(nèi)置校驗(yàn)的方式來(lái)構(gòu)建表單;模型驅(qū)動(dòng)模式采用自定義表單、自定義校驗(yàn)的方式來(lái)構(gòu)建表單。
表單指令
NgForm指令
NgForm指令是表單的控制中心,負(fù)責(zé)處理表單內(nèi)的頁(yè)面邏輯,為普通的表單元素?cái)U(kuò)充了許多額外的特性,所有的表單指令都需要在NgForm指令內(nèi)部才能正常運(yùn)行。
首先要在根模塊中添加FormsModule。
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { FormComponent } from './form.component';
@NgModule({
imports:[
BrowserModule,
FormsModule //導(dǎo)入FormsModule
],
declarations:[
AppComponent,
FormComponent
],
bootstrap:[AppComponent]
})
export class AppModule {}
可以不用在模板中顯式的使用NgForm指令,因?yàn)樘砑恿?code>FormsModule模塊后,angular模板在編譯解析時(shí),遇到<form>標(biāo)簽會(huì)自動(dòng)創(chuàng)建一個(gè)NgForm指令并且將其添加到該<form>標(biāo)簽上。
NgModel指令
angular表單支持單向和雙向數(shù)據(jù)綁定,表單的單向數(shù)據(jù)綁定使用[ngModel],雙向數(shù)據(jù)綁定使用[(ngModel)]。
<input type="text" name="contactName" [ngModel]="curContact.name" />
<input type="text" name="contactName" [(ngModel)]="curContact.name" />
在控件中使用NgModel屬性綁定,必須給該控件添加name屬性,否則會(huì)報(bào)錯(cuò)。因?yàn)?code>NgForm指令會(huì)為表單建立一個(gè)控件對(duì)象FormControl的集合,以此來(lái)作為表單控件的容器??丶?code>NgModel屬性綁定會(huì)以name作為唯一標(biāo)識(shí)符來(lái)注冊(cè)并生成一個(gè)FormControl,將其加入到FormControl的集合中。
常用的表單控件:
單選框:?jiǎn)芜x框控件實(shí)現(xiàn)雙向數(shù)據(jù)綁定,同一組單選框控件的所有[(ngModel)]屬性都必須綁定同一個(gè)模型數(shù)據(jù),且name屬性名也必須相同。
<input type="radio" name="sex" [(ngModel)]="curContact.sex" value="female" />女
<input type="radio" name="sex" [(ngModel)]="curContact.sex" value="male" />男
當(dāng)單擊選項(xiàng)為“女”的單選框,curContact.sex會(huì)被賦值為female,若單擊“男”單選框,則所對(duì)應(yīng)的值為male。
復(fù)選框:復(fù)選框控件用來(lái)表示該表單復(fù)選框是否被選中,其中[(ngModel)]屬性綁定的是一個(gè)布爾值。
<input type="checkbox" name="lock" [(ngModel)]="curContact.lock" />禁用
如果復(fù)選框被選中,則curContact.lock的值為true,否則為false。
單選下拉框:?jiǎn)芜x下拉框控件的雙向數(shù)據(jù)綁定需結(jié)合option元素綁定的值來(lái)實(shí)現(xiàn)。option選項(xiàng)的元素屬性的綁定目標(biāo)有兩種,分別為value和ngValue。當(dāng)在option元素中使用value綁定數(shù)據(jù)時(shí),其返回值類型是基本數(shù)據(jù)類型;當(dāng)使用ngValue綁定數(shù)據(jù)時(shí),其返回值類型是對(duì)象數(shù)據(jù)類型。
//定義下拉框所需所需數(shù)據(jù)
export class FormComponent {
interests:any[]=[
{value:'reading',display:'閱讀'},
{value:'traveling',display:'旅游'},
{value:'sport',display:'運(yùn)動(dòng)'}
];
}
<select name="interestValue" [(ngModel)]="curContact.interestValue">
<option *ngFor="let interest of interests" [value]="interest.value">
{{interest.display}}
</option>
</select>
例子中,使用[value]來(lái)綁定下拉選項(xiàng)的value屬性值,如果選中下拉選項(xiàng)框中的“旅游”項(xiàng),那么curContact.interestValue的值將變?yōu)?code>traveling。
單選框被選中后也可以返回對(duì)象類型的數(shù)據(jù)。
<select name="interestObj" [(ngModel)]="curContact.interestObj">
<option *ngFor="let interest of interests" [ngValue]="interest">
{{interest.display}}
</option>
</select>
當(dāng)選中某個(gè)項(xiàng)目后,該下拉框最終返回一個(gè)Object類型的對(duì)象數(shù)據(jù)。如選中“旅游”項(xiàng),則curContact.interestObj的值為{value:'traveling',display:'旅游'}。
多選下拉框:實(shí)現(xiàn)了下拉選擇多個(gè)選項(xiàng)的功能,返回的數(shù)據(jù)是一個(gè)由所有被選項(xiàng)數(shù)據(jù)組成的數(shù)組。
<select multiple name="interestMul" [(ngModel)]="curContact.interestMul">
<option *ngFor="let interest of interests" [value]="interest.value">
{{interest.display}}
</option>
</select>
多選下拉框使用[value]來(lái)綁定下拉選項(xiàng)的value屬性值,如果在多選下拉框中選中“旅游”和“運(yùn)動(dòng)”兩項(xiàng),則curContact.interestMul返回值為["traveling","sport"]。
多選下拉框還可以使用[ngValue]來(lái)綁定下拉選項(xiàng)的value屬性值,如果在多選下拉框中選中“旅游”和“運(yùn)動(dòng)”兩項(xiàng),那么curContact.interestMul返回的數(shù)組對(duì)象為[{value:'traveling',display:'旅游'},{value:'sport',display:'運(yùn)動(dòng)'}]。
模板局部變量
模板局部變量是模板中對(duì)DOM元素或指令(包括組件)的引用,可以使用在當(dāng)前元素、兄弟元素或任何子元素中。
DOM元素局部變量
若在標(biāo)簽元素中定義DOM元素局部變量,只需在其局部變量名前加上#或用ref-前綴。
<li>
<label for="name">姓名:</label>
<input type="text" #contactName name="contactName" id="contactName" />
<input type="number" ref-telNum name="telNum" id="telNum" />
<p>{{contactName.value}}--{{telNum.value}}</p>
</li>
angular會(huì)自動(dòng)把局部變量設(shè)置為對(duì)當(dāng)前DOM元素對(duì)象的引用,如上面的局部變量contactName引用的是document.getElementById("contactName")對(duì)象。在模板中定義局部變量后,可以直接在模板的其他元素中使用該元素的DOM屬性,如contactName.value和telNum.value。
表單指令局部變量
表單指令也可以定義局部變量,其引用方式與DOM元素局部變量的引用方式不同。表單指令的局部變量在定義時(shí)需手動(dòng)初始化為特定指令的代表值,最終解析后會(huì)被賦值為表單指令實(shí)例對(duì)象的引用。
NgForm表單局部變量
<form #contactForm="ngForm">
<ul>
<li>
<label for="contactName">姓名:</label>
<input type="text" name="contactName" [(ngModel)]="curContact.name" />
</li>
<li>
<label for="telNum">電話:</label>
<input type="text" name="telNum" [(ngModel)]="curContact.telNum" />
</li>
</ul>
</form>
局部變量contactForm為NgForm指令實(shí)例對(duì)象的引用,可以在模板中讀取NgForm實(shí)例對(duì)象的屬性值,如追蹤表單的valid屬性狀態(tài)。當(dāng)被包含的所有控件都有效時(shí),contactForm.valid的值為true,否則為false。在控件中添加ngModel和name屬性后,若往姓名控件中輸入“李四”,電話控件輸入“123456789”,則contactForm.value的值為:
{
name:"李四",
telNum:"123456789"
}
NgModel控件局部變量
<input type="text" name="contactName" [(ngModel)]="curContact.name"
#contactName="ngModel" />
<p>{{contactName.valid}}</p>
局部變量contactName是對(duì)NgModel指令實(shí)例對(duì)象的引用,可以在模板中讀取NgModel實(shí)例對(duì)象的屬性值,如通過(guò)contactName.valid可追蹤控件狀態(tài)、表單校驗(yàn)不通過(guò)時(shí)提示錯(cuò)誤信息。
表單狀態(tài)
表單NgForm和NgNodel指令都可以用于追蹤表單狀態(tài)來(lái)實(shí)現(xiàn)數(shù)據(jù)校驗(yàn)。表單NgForm和NgModel指令都有五個(gè)表示狀態(tài)的屬性,屬性值都為布爾類型,并都可通過(guò)對(duì)應(yīng)的局部變量獲取。NgForm追蹤的是整個(gè)表單控件的狀態(tài),NgModel追蹤的是其所在表單控件的狀態(tài)。

NgModelGroup指令
NgModelGroup指令可以對(duì)表單輸入內(nèi)容進(jìn)行分組,方便我們?cè)谡Z(yǔ)義上區(qū)分不同類型的輸入。
<form #concatForm="ngForm">
<fieldset ngModelGroup="nameGroup" #nameGroup="ngModelGroup">
<label>姓:</label>
<input type="text" name="firstname" [(ngModel)]="curContact.firstname" required />
<label>名字:</label>
<input type="text" name="lastname" [(ngModel)]="curContact.lastname" required />
</fieldset>
<fieldset ngModelGrooup="addressGroup" #addressGroup="ngModelGroup">
<label>街:</label>
<input type="text" name="street" [(ngModel)]="curContact.street" required />
<label>區(qū):</label>
<input type="text" name="zip" [(ngModel)]="curContact.zip" required />
<label>城市:</label>
<input type="text" name="city" [(ngModel)]="curContact.city" required />
</fieldset>
</form>
concatForm.value的值為:
{
nameGourp:{
firstname:'',
lastname:''
},
addressGroup:{
street:'',
zip:'',
city:''
}
}
NgModelGroup實(shí)例對(duì)象的valid屬性可以單獨(dú)校驗(yàn)其所在分組控件的輸入是否有效。如上例中只有當(dāng)curContact.firstname、curContact.lastname輸入都有效時(shí),局部變量nameGroup.valid才會(huì)變成true,否則為false。
ngSubmit事件
ngSubmit事件可以響應(yīng)表單里類型為submit的按鈕操作,并負(fù)責(zé)控制表單的提交流程。當(dāng)按鈕被單機(jī)后,會(huì)觸發(fā)表單的ngSubmit事件。
<form #contactForm="ngForm" (ngSubmit)="doSubmit(contactForm.value)">
<li class="form-group">
<button type="submit" class="btn btn-default" [disabled]="!contactForm.valid">添加</button>
<button type="reset" class="btn btn-default">重置</button>
</li>
</form>
export class FormComponent{
doSubmit(formValue:any){
//處理表單數(shù)據(jù)并提交
}
}
ngSubmit事件的類型是EventEmitter。當(dāng)提交按鈕被點(diǎn)擊后,首先執(zhí)行表單原生的onSubmit事件,接著執(zhí)行FormComponent組件中定義的doSubmit()方法,該方法接收contactForm.value的值作為參數(shù)傳入,并對(duì)傳入的數(shù)據(jù)進(jìn)行處理。
自定義表單樣式
NgModel指令不僅能追蹤表單控件的狀態(tài),還會(huì)根據(jù)表單控件的狀態(tài)使用對(duì)應(yīng)的CSS狀態(tài)類來(lái)更新表單控件的類名。表單控件包括6個(gè)CSS狀態(tài)類。

表單控件的CSS類名會(huì)根據(jù)表單控件狀態(tài)變化而變化。
.ng-valid[required]{
border-left:5px solid #0f0;
}
.ng-valid{
border-left:5px solid #f00;
}
還可結(jié)合控件的狀態(tài)屬性valid、pristine、dirty、touched、untouched等來(lái)添加有用的消息提示,以此來(lái)對(duì)用戶進(jìn)行有效的引導(dǎo)。
<div class="form-group">
<label for="contactName">姓名:</label>
<input type="type" class="form-control" minlength=3 maxlength=10
[(ngModel)]="curContact.name" name="contactName"
#contactName="ngModel" required />
<p [hidden]="contactName.valid||contactName.pristine" class="alert alert-invalid">
用戶名長(zhǎng)度3-10個(gè)字符
</p>
</div>
表單校驗(yàn)
表單內(nèi)置校驗(yàn)
angular支持的表單內(nèi)置校驗(yàn)包括:
- required:判斷表單控件值是否為空
- minlength:判斷表單控件值的最小長(zhǎng)度
- maxlength:判斷表單控件值的最大長(zhǎng)度
- pattern:判斷表單控件值的匹配規(guī)則
<input type="text" minlength=3 maxlength=10 [(ngModel)]="curContact.name" name="contactName" required />
可以在<form>標(biāo)簽中添加novalidate屬性來(lái)屏蔽HTML的攔截校驗(yàn)。
表單自定義校驗(yàn)
創(chuàng)建自定義校驗(yàn)
下面定義一個(gè)用戶名的校驗(yàn)器,校驗(yàn)規(guī)則為:用戶名必須是郵箱、手機(jī)號(hào)碼的格式。
//validate-username.ts
import { FormControl } from '@angular/forms';
const EMAIL_REGEXP=new RegExp("[a-z0-9]+@[a-z0-9]+.com");
const TEL_REGEXP=new RegExp("1[0-9]{10}");
export function validateUserName(c:FormControl){
return (EMAIL_REGEXP.test(c.value)||TEL_REGEXP.test(c.value))?null:{
userName:{
valid:false,
errorMsg:'用戶名必須是手機(jī)號(hào)或郵箱帳號(hào)'
}
}
}
自定義校驗(yàn)函數(shù)傳入的參數(shù)是FormControl類,通過(guò)對(duì)FormControl的value值進(jìn)行校驗(yàn)處理,返回校驗(yàn)結(jié)果。
使用自定義校驗(yàn)
使用模型驅(qū)動(dòng)方式構(gòu)建表單,需在表單組件所在的模塊代碼中導(dǎo)入ReactiveFormsModule,并在模塊的@NgModule元數(shù)據(jù)imports數(shù)組中加入ReactiveFormsModule。
import { ReactiveFormsModule } from '@angular/forms';
import { FormComponent } from './form.component';
import { AppComponent } from './app.component';
@NgModule({
imports:[BrowserModule,ReactiveFormsModule],
declarations:[AppComponent,FormComponent],
bootstrap:[AppComponent]
})
export class AppModule {}
導(dǎo)入ReactiveFormsModule后,可在表單組件FormComponent中使用模型驅(qū)動(dòng)方式構(gòu)建表單。
import { Component } from '@angular/core';
import { FormGroup,FormControl } from '@angular/forms';
import { validateUserName } from './validate-username';
@Component({
selector:'add-contact',
template:`
<form [formGroup]="customForm">
<label>姓名:</label>
<input type="text" formControlName="customName" />
</form>
`
})
export class FormComponent{
customForm=new FormGroup({
customName:new FormControl('',validateUserName)
});
}
在構(gòu)建FormControl實(shí)例對(duì)象customName時(shí)傳入的參數(shù)中,第一個(gè)參數(shù)為控件返回值的初始值,第二個(gè)參數(shù)為該控件的校驗(yàn)配置方法。
此外,校驗(yàn)配置可以使用Validators的內(nèi)置校驗(yàn),如Validators.required()等,這與直接在表單控件元素中添加required屬性效果一致。使用Validators內(nèi)置校驗(yàn),需先從@angular/forms導(dǎo)入Validators。
import { Validators } from '@angular/forms';
customForm=new FormGroup({
customName:new FormControl('',Validators.minLength(4))
});
如果需要在一個(gè)表單中添加多個(gè)校驗(yàn)器,可以在校驗(yàn)配置參數(shù)中使用數(shù)組,數(shù)組元素為對(duì)應(yīng)的校驗(yàn)方法。
customForm=new FormGroup({
customName:new FormControl('',[Validators.minLength(4),ValidateUserName])
});