vlambda博客
学习文章列表

响应式表单-Angular高级编程(6)

模板驱动表单中,form和单个控件之间是松关联的,分别对应组件中的不同对象。而在响应式表单(也叫模型驱动表单)中,整个form是一个树状模型,包含了表单中的所有控件,跟模板中一一对应。这个树状模型是通过FormGroup,FormControl,FormArray构建起来的。而表单验证也是在组件中通过代码进行的。


要使用响应式表单,首先要在app.module.ts中引入ReactiveFormModule

import { BrowserModule } from '@angular/platform-browser';import { NgModule } from '@angular/core';import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
@NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, FormsModule, ReactiveFormsModule ], providers: [], bootstrap: [AppComponent]})export class AppModule { }

然后修改模板,在form元素上定义formGroup属性为组件变量form,这个form变量要在组件中进行定义,他就是组件中代表了整个form树状结构的模型数据。然后在每个需要关联数据的控件上,通过formControlName指定控件的名字,对应于form模型树中的相应对象。formGroup是一组树形对象,可以嵌套。

<form novalidate [formGroup]="form" (ngSubmit)="onSubmit(form)"> <div> <input formControlName="str"/> <span> {{ formValue }} </span> </div> <div> <button type="submit"> 提交 </button> </div></form>

组件中定义根模型form。这个form可以通过FormGroup的构造函数初始化,也可以通过FormBuilder来构造。

import { Component } from '@angular/core';import {FormBuilder, FormControl, FormGroup} from "@angular/forms";@Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css']})export class AppComponent { form: FormGroup; formValue: string = '';
constructor(private fb: FormBuilder){    //两种初始化form的方式 //this.form = this.fb.group({str: ''}); this.form = new FormGroup({'str': new FormControl()}); }
onSubmit(form){ this.formValue = JSON.stringify(this.form.value); }}


运行后如图所示,可以看到form树状模型包含表单中的相对应控件的值。


下面我们给这个表单加上验证功能,无论是用FormControl构造函数,还是FormBuilder,我们都可以将Validators加入到参数中进行构造。可以通过control的errors取得错误的具体信息。

import { Component } from '@angular/core';import {FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms";@Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css']})export class AppComponent { form: FormGroup; formValue: string = ''; errMsg: string = '';
constructor(private fb: FormBuilder){ // 两种方法构建form模型树 //this.form = this.fb.group({str: ['', Validators.compose([Validators.required, Validators.minLength(3)])]}); this.form = new FormGroup({'str': new FormControl('', Validators.compose([Validators.required, Validators.minLength(3)]))}); }
onSubmit(form){ this.errMsg = ''; if(this.form.valid){ this.formValue = JSON.stringify(this.form.value); }else{ if(this.form.controls['str'].errors.required){ this.errMsg = "必填"; }else if(this.form.controls['str'].errors.minlength){ this.errMsg = `最小字符数:${this.form.controls['str'].errors.minlength.requiredLength}`; } } }}

模板文件增加错误信息展示

<form novalidate [formGroup]="form" (ngSubmit)="onSubmit(form)"> <div> <input formControlName="str"/> <span> {{ formValue }} </span> </div> <div> <button type="submit"> 提交 </button> </div> <div *ngIf="errMsg"> {{ errMsg }} </div></form>

响应式表单-Angular高级编程(6)

str input对应的是一个FormControl控件,如果在构造时不指定验证器,也可以通过setValidator方法随后给控件加上验证功能,效果是一样的。

constructor(){ this.form = new FormGroup({'str': new FormControl('')});  this.form.controls['str'].setValidators([Validators.compose([Validators.required, Validators.minLength(3)])]);}

可以创建自定义验证器。新建limit.formvalidator.ts文件,定义如下代码,LimitValidator类的Limit静态方法返回一个验证器,如果不出错,返回null,如果出错,返回一个错误信息对象。

import { FormControl } from '@angular/forms';
export class LimitValidator{ static Limit(limit: number){ return (control: FormControl): {[key: string] : any} => { let val = Number(control.value); if(val != NaN && val > limit){ return {"limit": {"limit": limit, "actualValue": val}}; }else{ return null; } } }}

使用自定义验证器跟使用内置验证器一样。

constructor(){ ... this.form.controls['str'].setValidators([Validators.compose([Validators.required, Validators.minLength(3), LimitValidator.Limit(5)])]);}
onSubmit(form){ this.errMsg = ''; if(this.form.valid){ this.formValue = JSON.stringify(this.form.value); }else{ if(this.form.controls['str'].errors.required){ this.errMsg = "必填"; }else if(this.form.controls['str'].errors.minlength){ this.errMsg = `最小字符数:${this.form.controls['str'].errors.minlength.requiredLength}`; }else if(this.form.controls['str'].errors.limit){ this.errMsg = `超过了最大值:${this.form.controls['str'].errors.limit.limit}, 当前值:${this.form.controls['str'].errors.limit.actualValue}` } } }

FormGroup(树状模型,类似于对象), FormArray(数组模型)和FormControl还有两个赋值函数经常使用:setValue和patchValue。这两个函数都可以给上面3个对象赋值。来看一下这两个函数在FormGroup中的定义,FormArray中的定义类似。

setValue(value: { [key: string]: any; }, options?: { onlySelf?: boolean; emitEvent?: boolean; }): void;
patchValue(value: { [key: string]: any; }, options?: { onlySelf?: boolean; emitEvent?: boolean; }): void;


可以看到两个函数的定义是一样的。都接受两个参数,第一个参数value,是一个key-value对,代表要更新的控件及其值。第二个options是一个可选项,如果onlySelf设置为false是默认行为,true的话,控件更新值后将不会向上触发父节点直到根节点的验证。当值更改时,控件默认会emit ValueChanges事件,当验证状态有变化时,默认会触发StatusChanges事件,如果把emitEvent设置为false,则会改变默认行为,这两个事件都不会触发。


setValue和patchValue的区别在于:对于FormGroup包含多个FormGroup和多个FormControl的情况,setValue的value值必须包含FormGroup所有属性及其值,否则会报错。而patchValue的value只需要包含需要更新的属性和值。