0%

Angular2 响应式表单验证

前面一文介绍了模板驱动表单的验证,但它的功能比较简单,有时无法满足我们的需求。响应式表单与模板驱动表单不同的是,响应式表单在组件类中创建表单控制器模型,可在组件中随意控制校验规则。

响应式表单使用ReactiveFormsModule,而非普通的FormModule,需要在app.module.ts里导入

import { ReactiveFormsModule} from '@angular/forms';

@NgModule({
  declarations: [
    ...
  ],
  exports:[AppComponent],
  imports: [
    BrowserModule,
    ...
    ReactiveFormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

ReactiveFormsModule包含FormControlDirective、FormGroupDirective、FormControlName、FormArrayName和InternalFormsSharedModule模块里的指令。

  • FormControlDirective 描述表单的一个字段
  • FormGroupDirective 描述表单分组
  • FormControlName 描述表单字段名
  • FormArrayName 描述同类型的一组数据的名称,与表单分组无关

下面的例子与前文模板驱动表单类似,不过换了一种实现。自定义校验器也一样,代码不再贴。

模板:

<form [formGroup]="registerForm">
  <div>
    <label for="userName">用户名:</label>
    <!--给input设置一个本地变量,可以读取errors显示错误信息-->
    <input type="text" id="userName" name="userName"  formControlName="userName" required>
    <span *ngIf="formErrors['userName']" class="error">
    { { formErrors['userName'] }}
    </span>

  </div>
  <fieldset aria-required="true" formGroupName="passwordGroup" required>
    <label for="password1">密码:</label>
    <input type="password" id="password1" name="password1" formControlName="password1" required (keyup)="password1ValChanged()"  >
    <span *ngIf="formErrors['passwordGroup.password1']" class="error">
    { { formErrors['passwordGroup.password1'] }}
    </span>
    <label for="password2">重复密码:</label>
    <input type="password" id="password2" name="password2" formControlName="password2" required>
    <span *ngIf="formErrors['passwordGroup.password2']" class="error">
    { { formErrors['passwordGroup.password2'] }}
  </span>
  </fieldset>

  <div>
    <label for="email">邮箱:</label>
    <input type="text" id="email" name="email" formControlName="email" required>
    <!-- 可以通过表单的onValueChanged事件,读到当前的错误信息,写到指定字段里 -->
    <div *ngIf="formErrors.email" class="error">
      { { formErrors.email }}
    </div>
  </div>
  <div>
    <label>性别:</label>
    <input type="radio" name="sex"  value="male" formControlName="sex"> 男
    <input type="radio" name="sex"  value="female" formControlName="sex" > 女
  </div>
  <fieldset formGroupName="nameGroup">
    <label>姓:</label>
    <input type="text" name="firstName" formControlName="firstName" required checked="checked"><br />
    <label>名:</label>
    <input type="text" name="lastName" formControlName="lastName">
  </fieldset>

  <button type="button" class="btn btn-default"
          [disabled]="!registerForm.valid" (click)="doSubmit(registerForm.value)">注册</button>
</form>

{ {registerForm.value|json}}

组件:

import {Component, OnInit, AfterViewInit} from "@angular/core";
import {FormGroup, FormControl, FormBuilder, Validators} from "@angular/forms";
import {repeatPassword} from "../repeat-password.directive";

@Component({
  selector: 'app-reactive-form',
  templateUrl: './reactive-form.component.html',
  styleUrls: ['./reactive-form.component.css']
})
export class ReactiveFormComponent implements OnInit,AfterViewInit {
  registerForm: FormGroup;

  ngAfterViewInit(): void {

  }

  constructor(private fb: FormBuilder) {
  }

  ngOnInit() {
    this.registerForm = this.fb.group({
      //每一个input都是一个FormControl,key是formControlName,value是构建FormControl的参数,
      // 第一个参数是input的默认值,第二个参数是校验器数组
      'userName': ['', [Validators.required,Validators.minLength(4)]],
      //分组的FormControl,也需要分组构建,key是formGroupName
      'passwordGroup':this.fb.group({
        'password1': ['', [Validators.required,Validators.minLength(4)]],
      }),
      'nameGroup':this.fb.group({
        'firstName': ['', [Validators.required]],
        'lastName': ['', [Validators.required]],
      }),
      'email':['',[Validators.required,Validators.pattern("[\\w]+?@[\\w]+?\\.[a-z]+?")]],
      //默认选中男性
      'sex':['male',[Validators.required]],


    });
    let passwordFormGroup=this.registerForm.controls['passwordGroup'] as FormGroup;
    let password1Control=passwordFormGroup.controls['password1'] as FormControl;

    passwordFormGroup.addControl('password2',new FormControl('',[Validators.required,repeatPassword(password1Control)]));

    this.registerForm.valueChanges.subscribe(data => this.onValueChanged(data));

  }
  //存储错误信息
  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':'性别必填'
    }

  };

  /**
   * 第一个密码改变时,清空第二个密码框
   */
  password1ValChanged() {
    (this.registerForm.controls['passwordGroup'] as FormGroup).controls['password2'].reset();
  }

  /**
   * 表单值改变时,重新校验
   * @param data
   */
  onValueChanged(data) {

    for (const field in this.formErrors) {
      this.formErrors[field] = '';
      //取到表单字段
      const control = this.registerForm.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] + '';
        }
      }

    }

  }

  doSubmit(obj: any) {
    //表单提交
    console.log(JSON.stringify(obj));
  }



}