响应式表单-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";({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";selector: ,templateUrl: ,styleUrls: []})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>
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只需要包含需要更新的属性和值。
