angular提供了模板驅動和模型驅動兩種方式來構建表單。模板驅動模式使用模板表單內置指令、內置校驗的方式來構建表單;模型驅動模式采用自定義表單、自定義校驗的方式來構建表單。
表單指令
NgForm指令
NgForm
指令是表單的控制中心,負責處理表單內的頁面邏輯,為普通的表單元素擴充了許多額外的特性,所有的表單指令都需要在NgForm
指令內部才能正常運行。
首先要在根模塊中添加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 //導入FormsModule
],
declarations:[
AppComponent,
FormComponent
],
bootstrap:[AppComponent]
})
export class AppModule {}
可以不用在模板中顯式的使用NgForm
指令,因為添加了FormsModule
模塊后,angular模板在編譯解析時,遇到<form>
標簽會自動創建一個NgForm
指令并且將其添加到該<form>
標簽上。
NgModel指令
angular表單支持單向和雙向數據綁定,表單的單向數據綁定使用[ngModel]
,雙向數據綁定使用[(ngModel)]
。
<input type="text" name="contactName" [ngModel]="curContact.name" />
<input type="text" name="contactName" [(ngModel)]="curContact.name" />
在控件中使用NgModel
屬性綁定,必須給該控件添加name
屬性,否則會報錯。因為NgForm
指令會為表單建立一個控件對象FormControl
的集合,以此來作為表單控件的容器。控件的NgModel
屬性綁定會以name
作為唯一標識符來注冊并生成一個FormControl
,將其加入到FormControl
的集合中。
常用的表單控件:
單選框:單選框控件實現雙向數據綁定,同一組單選框控件的所有[(ngModel)]
屬性都必須綁定同一個模型數據,且name
屬性名也必須相同。
<input type="radio" name="sex" [(ngModel)]="curContact.sex" value="female" />女
<input type="radio" name="sex" [(ngModel)]="curContact.sex" value="male" />男
當單擊選項為“女”的單選框,curContact.sex
會被賦值為female
,若單擊“男”單選框,則所對應的值為male
。
復選框:復選框控件用來表示該表單復選框是否被選中,其中[(ngModel)]
屬性綁定的是一個布爾值。
<input type="checkbox" name="lock" [(ngModel)]="curContact.lock" />禁用
如果復選框被選中,則curContact.lock
的值為true
,否則為false
。
單選下拉框:單選下拉框控件的雙向數據綁定需結合option
元素綁定的值來實現。option
選項的元素屬性的綁定目標有兩種,分別為value
和ngValue
。當在option
元素中使用value
綁定數據時,其返回值類型是基本數據類型;當使用ngValue
綁定數據時,其返回值類型是對象數據類型。
//定義下拉框所需所需數據
export class FormComponent {
interests:any[]=[
{value:'reading',display:'閱讀'},
{value:'traveling',display:'旅游'},
{value:'sport',display:'運動'}
];
}
<select name="interestValue" [(ngModel)]="curContact.interestValue">
<option *ngFor="let interest of interests" [value]="interest.value">
{{interest.display}}
</option>
</select>
例子中,使用[value]
來綁定下拉選項的value
屬性值,如果選中下拉選項框中的“旅游”項,那么curContact.interestValue
的值將變為traveling
。
單選框被選中后也可以返回對象類型的數據。
<select name="interestObj" [(ngModel)]="curContact.interestObj">
<option *ngFor="let interest of interests" [ngValue]="interest">
{{interest.display}}
</option>
</select>
當選中某個項目后,該下拉框最終返回一個Object
類型的對象數據。如選中“旅游”項,則curContact.interestObj
的值為{value:'traveling',display:'旅游'}
。
多選下拉框:實現了下拉選擇多個選項的功能,返回的數據是一個由所有被選項數據組成的數組。
<select multiple name="interestMul" [(ngModel)]="curContact.interestMul">
<option *ngFor="let interest of interests" [value]="interest.value">
{{interest.display}}
</option>
</select>
多選下拉框使用[value]
來綁定下拉選項的value
屬性值,如果在多選下拉框中選中“旅游”和“運動”兩項,則curContact.interestMul
返回值為["traveling","sport"]
。
多選下拉框還可以使用[ngValue]
來綁定下拉選項的value
屬性值,如果在多選下拉框中選中“旅游”和“運動”兩項,那么curContact.interestMul
返回的數組對象為[{value:'traveling',display:'旅游'},{value:'sport',display:'運動'}]
。
模板局部變量
模板局部變量是模板中對DOM元素或指令(包括組件)的引用,可以使用在當前元素、兄弟元素或任何子元素中。
DOM元素局部變量
若在標簽元素中定義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會自動把局部變量設置為對當前DOM元素對象的引用,如上面的局部變量contactName
引用的是document.getElementById("contactName")
對象。在模板中定義局部變量后,可以直接在模板的其他元素中使用該元素的DOM屬性,如contactName.value
和telNum.value
。
表單指令局部變量
表單指令也可以定義局部變量,其引用方式與DOM元素局部變量的引用方式不同。表單指令的局部變量在定義時需手動初始化為特定指令的代表值,最終解析后會被賦值為表單指令實例對象的引用。
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
指令實例對象的引用,可以在模板中讀取NgForm
實例對象的屬性值,如追蹤表單的valid
屬性狀態。當被包含的所有控件都有效時,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
是對NgModel
指令實例對象的引用,可以在模板中讀取NgModel
實例對象的屬性值,如通過contactName.valid
可追蹤控件狀態、表單校驗不通過時提示錯誤信息。
表單狀態
表單NgForm
和NgNodel
指令都可以用于追蹤表單狀態來實現數據校驗。表單NgForm
和NgModel
指令都有五個表示狀態的屬性,屬性值都為布爾類型,并都可通過對應的局部變量獲取。NgForm
追蹤的是整個表單控件的狀態,NgModel
追蹤的是其所在表單控件的狀態。
NgModelGroup指令
NgModelGroup
指令可以對表單輸入內容進行分組,方便我們在語義上區分不同類型的輸入。
<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>區:</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
實例對象的valid
屬性可以單獨校驗其所在分組控件的輸入是否有效。如上例中只有當curContact.firstname
、curContact.lastname
輸入都有效時,局部變量nameGroup.valid
才會變成true
,否則為false
。
ngSubmit事件
ngSubmit
事件可以響應表單里類型為submit
的按鈕操作,并負責控制表單的提交流程。當按鈕被單機后,會觸發表單的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){
//處理表單數據并提交
}
}
ngSubmit
事件的類型是EventEmitter
。當提交按鈕被點擊后,首先執行表單原生的onSubmit
事件,接著執行FormComponent
組件中定義的doSubmit()
方法,該方法接收contactForm.value
的值作為參數傳入,并對傳入的數據進行處理。
自定義表單樣式
NgModel
指令不僅能追蹤表單控件的狀態,還會根據表單控件的狀態使用對應的CSS狀態類來更新表單控件的類名。表單控件包括6個CSS狀態類。
表單控件的CSS類名會根據表單控件狀態變化而變化。
.ng-valid[required]{
border-left:5px solid #0f0;
}
.ng-valid{
border-left:5px solid #f00;
}
還可結合控件的狀態屬性valid
、pristine
、dirty
、touched
、untouched
等來添加有用的消息提示,以此來對用戶進行有效的引導。
<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">
用戶名長度3-10個字符
</p>
</div>
表單校驗
表單內置校驗
angular支持的表單內置校驗包括:
- required:判斷表單控件值是否為空
- minlength:判斷表單控件值的最小長度
- maxlength:判斷表單控件值的最大長度
- pattern:判斷表單控件值的匹配規則
<input type="text" minlength=3 maxlength=10 [(ngModel)]="curContact.name" name="contactName" required />
可以在<form>
標簽中添加novalidate
屬性來屏蔽HTML的攔截校驗。
表單自定義校驗
創建自定義校驗
下面定義一個用戶名的校驗器,校驗規則為:用戶名必須是郵箱、手機號碼的格式。
//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:'用戶名必須是手機號或郵箱帳號'
}
}
}
自定義校驗函數傳入的參數是FormControl
類,通過對FormControl
的value
值進行校驗處理,返回校驗結果。
使用自定義校驗
使用模型驅動方式構建表單,需在表單組件所在的模塊代碼中導入ReactiveFormsModule
,并在模塊的@NgModule
元數據imports
數組中加入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 {}
導入ReactiveFormsModule
后,可在表單組件FormComponent
中使用模型驅動方式構建表單。
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)
});
}
在構建FormControl
實例對象customName
時傳入的參數中,第一個參數為控件返回值的初始值,第二個參數為該控件的校驗配置方法。
此外,校驗配置可以使用Validators
的內置校驗,如Validators.required()
等,這與直接在表單控件元素中添加required
屬性效果一致。使用Validators
內置校驗,需先從@angular/forms
導入Validators
。
import { Validators } from '@angular/forms';
customForm=new FormGroup({
customName:new FormControl('',Validators.minLength(4))
});
如果需要在一個表單中添加多個校驗器,可以在校驗配置參數中使用數組,數組元素為對應的校驗方法。
customForm=new FormGroup({
customName:new FormControl('',[Validators.minLength(4),ValidateUserName])
});