响应式表单-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只需要包含需要更新的属性和值。