模板驅動表單,指的是經過html5標準校驗的表單,優點在於使用起來簡單,但要動態修改驗證器、操縱控制器模型不是很方便。css
Angular2對錶單處理作了一系列封裝(模板驅動表單以及響應式表單):html
-
數據綁定html5
這個天然不用說,使用ngModel能夠雙向綁定到組件裏的對象字段。ajax
-
控件狀態檢測正則表達式
Angular會自動根據控件狀態加上相應的class,若是咱們須要編輯input標籤在不一樣狀態下的樣式,只須要在css裏寫相應的類就能夠了。json
狀態 true時的css類 false時的css類 控件是否被訪問過 ng-touched ng-untouched 控件值是否已經變化 ng-dirty ng-pristine 控件值是否有效 ng-valid ng-invalid -
表單校驗bootstrap
模板驅動表單支持html5標準屬性校驗:app
- required:必填
- minlength:最小長度
- maxlength:最大長度
- pattern:正則表達式校驗
另外支持自定義Validator.ide
響應式表單內置了上面四種Validator,也能夠本身擴展。ui
模板驅動表單相關指令封裝在FormsModule模塊中,app.module.ts裏須要先導入:
import {FormsModule} from '@angular/forms';
@NgModule({
declarations: [
AppComponent,
...
],
exports:[AppComponent],
imports: [
BrowserModule,
FormsModule,
HttpModule,
...
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
FormsModule模塊包含NgModule、NgModuleGroup、NgForm和InternalFormsSharedModule模塊中包含的指令。
- NgForm 標記一個表單
- NgModelGroup 字段分組
- NgModel 字段
InternalFormsSharedModule是Angular內部模塊,FormsModule和ReactiveFormsModule都引用了它,因此能夠不用顯式引入,直接使用。
下面的例子演示了一個模板驅動表單,包括表單校驗、字段分組、控件狀態、數據綁定,以及自定義校驗器。自定義校驗器的功能是校驗第二個密碼是否與第一個密碼相同。
自定義校驗器:
repeat-password.directive.ts:
import {Directive, Input, OnChanges, SimpleChanges} from '@angular/core'; import {NG_VALIDATORS, FormControl, Validator, AbstractControl, ValidatorFn, NgModel} from "@angular/forms"; /** * 自定義指令,用於檢驗input標籤的值是否跟指定input的值標籤相同 */ @Directive({ selector: '[repeatPassword]', providers: [{provide: NG_VALIDATORS, useExisting: RepeatPasswordDirective, multi: true}] }) export class RepeatPasswordDirective implements Validator,OnChanges{ /** * 校驗方法 * @param c * @returns {{[p: string]: any}} */ validate(c: AbstractControl): {[p: string]: any} { return verifyPassword(c,this.repeatPassword.control); } ngOnChanges(changes: SimpleChanges): void { this.repeatPassword=changes['repeatPassword'].currentValue; } /** * 經過屬性傳入另外一個input標籤的model * 名稱與選擇器一致,就不須要在使用的時候加額外的屬性傳入 */ @Input() repeatPassword:NgModel; constructor() { } } /** * 導出校驗方法,供響應式表單使用 * @param password1Controller * @returns {(currentControl:AbstractControl)=>{[p: string]: any}} */ export function repeatPassword(password1Controller:FormControl):ValidatorFn { return (currentControl: AbstractControl): {[key: string]: any} => { return verifyPassword(currentControl,password1Controller); }; } function verifyPassword(currentControl: AbstractControl,password1Controller:FormControl):{[key: string]: any} { if(!password1Controller.valid) { console.log("密碼1無效"); return {password1InValid:{'errorMsg':''}} } if((!currentControl.untouched||currentControl.dirty)&&password1Controller.value!=currentControl.value) { return {passwordNEQ:{'errorMsg':'兩次密碼輸入不一致!'}} } }
建立指令後別忘了在app.module.ts裏引入 :
@NgModule({
declarations: [
AppComponent,
...,
RepeatPasswordDirective
],
exports:[AppComponent],
imports: [
BrowserModule,
FormsModule,
HttpModule,
...
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
固然,若是使用ng g directive repeatPassword
命令建立指令,會自動添加。
模板:
<!--(ngSubmit)綁定的表單提交事件,ajax不須要--> <form #registerForm="ngForm" (ngSubmit)="doSubmit(registerForm.value)" > <div> <label for="userName">用戶名:</label> <!--給input設置一個本地變量,能夠讀取errors顯示錯誤信息--> <input type="text" id="userName" name="userName" [(ngModel)]="formData.userName" #userName="ngModel" required minlength="4"> <div *ngIf="userName.errors && (userName.dirty || userName.touched)" class="error"> <span [hidden]="!userName.errors.required">用戶名必須輸入</span> <span [hidden]="!userName.errors.minlength">用戶名至少4位</span> </div> </div> <!--ngModelGroup指令能夠給表單字段分組,值password是registerForm.value裏該組的字段名,#passwordGroup是該組的本地變量名--> <fieldset ngModelGroup="passwordGroup" #passwordGroup="ngModelGroup" aria-required="true"> <label for="password1">密碼:</label> <input type="password" id="password1" name="password1" [(ngModel)]="formData.password1" #password1="ngModel" required minlength="8"> <label for="password2">重複密碼:</label> <!--使用自定義的校驗器,加入repeatPassword指令,傳入第一個密碼輸入框的ngModel,即用#password1="ngModel"聲明的password1--> <input type="password" id="password2" name="password2" [(ngModel)]="formData.password2" [repeatPassword]="password1"> <span *ngIf="formErrors['passwordGroup.password2']" class="error"> {{ formErrors['passwordGroup.password2'] }} </span> </fieldset> <div> <label for="email">郵箱:</label> <input type="text" id="email" name="email" [(ngModel)]="formData.email" required pattern="[\w]+?@[\w]+?\.[a-z]+?"> <!-- 能夠經過表單的onValueChanged事件,讀到當前的錯誤信息,寫到指定字段裏 --> <div *ngIf="formErrors.email" class="error"> {{ formErrors.email }} </div> </div> <div> <label>性別:</label> <input type="radio" name="sex" [(ngModel)]="formData.sex" value="male" checked="checked"> 男 <input type="radio" name="sex" [(ngModel)]="formData.sex" value="female" > 女 </div> <fieldset ngModelGroup="nameGroup" #nameGroup="ngModelGroup"> <label>姓:</label> <input type="text" name="firstName" [(ngModel)]="formData.firstName" required><br /> <label>名:</label> <input type="text" name="lastName" [(ngModel)]="formData.lastName"> </fieldset> <button type="button" class="btn btn-default" [disabled]="!registerForm.valid" (click)="doSubmit(registerForm.value)">註冊</button> </form> {{registerForm.value|json}}
組件:
import {Component, OnInit, ViewChild, AfterViewInit} from "@angular/core"; import {NgForm} from "@angular/forms"; @Component({ selector: 'app-form', templateUrl: './form.component.html', styleUrls: ['./form.component.css'] }) export class FormComponent implements OnInit,AfterViewInit { ngAfterViewInit(): void { //訂閱表單值改變事件 this.registerForm.valueChanges.subscribe(data => this.onValueChanged(data)); } //找到表單 @ViewChild('registerForm') registerForm: NgForm; constructor() { } formData = {} as any; ngOnInit() { //默認性別爲male this.formData.sex = "male"; } doSubmit(obj: any) { //表單提交 console.log(JSON.stringify(obj)); } onValueChanged(data) { for (const field in this.formErrors) { this.formErrors[field] = ''; //取到表單字段 const control = this.registerForm.form.get(field); //表單字段已修改或無效 if (control && control.dirty && !control.valid) { //取出對應字段可能的錯誤信息 const messages = this.validationMessages[field]; //從errors裏取出錯誤類型,再拼上該錯誤對應的信息 for (const key in control.errors) { this.formErrors[field] += messages[key] + ''; } } } } //存儲錯誤信息 formErrors = { 'email': '', 'userName': '', 'passwordGroup.password1':'', 'passwordGroup.password2':'', 'sex':'' }; //錯誤對應的提示 validationMessages = { 'email': { 'required': '郵箱必須填寫.', 'pattern': '郵箱格式不對', }, 'userName': { 'required': '用戶名必填.', 'minlength': '用戶名過短', }, 'passwordGroup.password1':{ 'required': '請輸入密碼', 'minlength': '密碼過短', }, 'passwordGroup.password2':{ 'required': '請重複輸入密碼', 'minlength': '密碼過短', 'passwordNEQ':'兩次輸入密碼不一樣', 'password1InValid':'' }, 'sex':{ 'required':'性別必填' } }; }