Angular2 模板驅動表單校驗

 

模板驅動表單,指的是經過html5標準校驗的表單,優點在於使用起來簡單,但要動態修改驗證器、操縱控制器模型不是很方便。css

 

Angular2對錶單處理作了一系列封裝(模板驅動表單以及響應式表單):html

  1. 數據綁定html5

    這個天然不用說,使用ngModel能夠雙向綁定到組件裏的對象字段。ajax

  2. 控件狀態檢測正則表達式

    Angular會自動根據控件狀態加上相應的class,若是咱們須要編輯input標籤在不一樣狀態下的樣式,只須要在css裏寫相應的類就能夠了。json

    狀態 true時的css類 false時的css類
    控件是否被訪問過 ng-touched ng-untouched
    控件值是否已經變化 ng-dirty ng-pristine
    控件值是否有效 ng-valid ng-invalid
  3. 表單校驗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':'性別必填'
    }

  };
}
相關文章
相關標籤/搜索