Angular响应式表单
Angular响应式表单是建立在RxJS基础上,有很多接口是使用RxJs接口实现
一.响应式表单基础元件
1.项目引入
- 在
@angular/forms
中引入ReactiveFormsModule - 响应式表单基础元件: FormControl [来自
@angular/forms
]
2.项目构建
-
创建FormControl对象
import { Component } from '@angular/core'; import { FormControl } from '@angular/forms'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { user = new FormControl(); }
-
使用定义的对象
<input type="text" [formControl]="user" /> <div> {{user.value | json}} </div>
-
效果:当在输入框输入内容时,会直接在页面中进行显示
3.组件中获取和更新控件的值
-
可以通过监听FormControl的valueChanges方法获取对应值,也可以通过setValue方法设置对应值
import { Component, OnInit } from '@angular/core'; import { FormControl } from '@angular/forms'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit{ user = new FormControl(); ngOnInit() { // 由于FormControl的valueChanges方法返回的是Observable对象 // 需要使用subscribe的方式进行监听 this.user.valueChanges.subscribe((val)=>{ console.log(val); }); } changeUserName() { this.user.setValue('Jack'); //通过FormControl的setValue方法可以设置对应的值 } }
-
在模板中进行显示
<input type="text" [formControl]="user"/> <button (click)="changeUserName()">Click</button> <div> {{user.value | json}} </div>
二.使用FormGroup创建表单
1.项目引入
- 在
@angular/forms
中引入FormGroup和FormControl
2.实例代码
-
ts中定义FormGroup对象,并包含多个FormControl对象;声明onSubmit方法用于打印FormGroup中的数据信息
import { Component, OnInit } from '@angular/core'; import { FormGroup, FormControl } from '@angular/forms'; @Component({ selector: 'app-FormGroupTest', templateUrl: './FormGroupTest.component.html', styleUrls: ['./FormGroupTest.component.css'] }) export class FormGroupTestComponent { userInfo = new FormGroup({ userName: new FormControl(), phone: new FormControl(), sex: new FormControl() }); onSubmit() { console.log(this.userInfo.value); } }
-
html中使用
[formGroup]
绑定ts中的对象,通过angular自带的form标签的提交事件ngSubmit
设置点击submit按钮时的触发事件<form [formGroup]="userInfo" (ngSubmit)="onSubmit()"> <input type="text" formControlName="userName"/> <input type="text" formControlName="phone"/> <input type="radio" formControlName="sex" value="1"/>男 <input type="radio" formControlName="sex" value="0"/>女 <button type="submit">提交</button> </form>
-
此时点击提交后打印的内容就是表单中实际值
3.对表单字段进行分组
-
如果想声明嵌套表单,则可以通过FormGroup中嵌套FormGroup的方式进行实现
-
TypeScript中实现内容
import { Component, OnInit } from '@angular/core'; import { FormGroup, FormControl } from '@angular/forms'; @Component({ selector: 'app-FormGroupTest', templateUrl: './FormGroupTest.component.html', styleUrls: ['./FormGroupTest.component.css'] }) export class FormGroupTestComponent { userInfo = new FormGroup({ userName: new FormControl(), phone: new FormControl(), sex: new FormControl(), pwd: new FormGroup({ password: new FormControl(), confirmPassword: new FormControl() }) }); onSubmit() { console.log(this.userInfo.value); } }
-
Html中在form中添加form并设置属性名为formGroupName
<form [formGroup]="userInfo" (ngSubmit)="onSubmit()"> <input type="text" formControlName="userName"/> <input type="text" formControlName="phone"/> <input type="radio" formControlName="sex" value="1"/>男 <input type="radio" formControlName="sex" value="0"/>女 <form formGroupName="pwd"> <input type="text" formControlName="password"/> <input type="text" formControlName="confirmPassword"/> </form> <button type="submit">提交</button> </form>
-
输出内容为嵌套对象的形式
{ phone: "180411", pwd: {password: "123456", confirmPassword: "123456"}, sex: "1", userName: "jack" }
4.更新表单中字段
-
注意:如果使用FormGroup的方式声明表单,再使用
form.setValue()
方法时需要设置所有表单值,如设置不全则会进行报错 -
更新表单指定字段方式一:使用
form.patchValue(键值对象);
方法设置表单中指定字段的值 -
更新表单指定字段方式二:先使用
form.get(键)
获取formControl对象,再调用setValue方法:,如this.userInfo.get('userName').setValue('Jerry');
-
实例代码
-
TypeScript
import { Component, OnInit } from '@angular/core'; import { FormGroup, FormControl } from '@angular/forms'; @Component({ selector: 'app-FormGroupTest', templateUrl: './FormGroupTest.component.html', styleUrls: ['./FormGroupTest.component.css'] }) export class FormGroupTestComponent { userInfo = new FormGroup({ userName: new FormControl(), phone: new FormControl(), sex: new FormControl(), pwd: new FormGroup({ password: new FormControl(), confirmPassword: new FormControl() }) }); onSubmit() { console.log(this.userInfo.value); } updateFormData() { // this.userInfo.patchValue({'userName':'Jack'}); this.userInfo.get('userName').setValue('Jerry'); } }
-
HTML
<form [formGroup]="userInfo" (ngSubmit)="onSubmit()"> <input type="text" formControlName="userName"/> <input type="text" formControlName="phone"/> <input type="radio" formControlName="sex" value="1"/>男 <input type="radio" formControlName="sex" value="0"/>女 <form formGroupName="pwd"> <input type="text" formControlName="password"/> <input type="text" formControlName="confirmPassword"/> </form> <button type="submit">提交</button> <button (click)="updateFormData()">更新表单</button> </form>
-
5.使用内置验证器验证表单字段
-
可以通过给FormControl构造函数的第一个参数设置表单字段的默认值,第二个参数设置验证器类型如required、最小最大长等
-
通过声明get方法的方式指定对应get的属性值,方便在html中直接调用
-
通过FromControl的invalid的方法可以判断表单字段是否通过,touched和dirty方法指定当触发之后再回执行判断,hasError方法设置判断对应类型的错误
-
TypeScript代码
import { Component, OnInit } from '@angular/core'; import { FormGroup, FormControl, Validators, AbstractControl } from '@angular/forms'; @Component({ selector: 'app-FormGroupTest', templateUrl: './FormGroupTest.component.html', styleUrls: ['./FormGroupTest.component.css'] }) export class FormGroupTestComponent { userInfo = new FormGroup({ // 参数1: 设置默认值; 参数2:设置校验器 userName: new FormControl('', [Validators.required, Validators.minLength(3), Validators.maxLength(5)]), phone: new FormControl('', [Validators.required, Validators.pattern(/^1[34578]\d{9}$/)]), sex: new FormControl('1', [Validators.required]), pwd: new FormGroup({ password: new FormControl(), confirmPassword: new FormControl() }) }); onSubmit() { console.log(this.userInfo.value); } updateFormData() { // this.userInfo.patchValue({'userName':'Jack'}); this.userInfo.get('userName').setValue('Jerry'); } // 定义get方法直接获取属性值 get userName(): AbstractControl { return this.userInfo.get('userName'); } get phone(): AbstractControl { return this.userInfo.get('phone'); } }
-
HTML代码
<form [formGroup]="userInfo" (ngSubmit)="onSubmit()"> <fieldset> <label>账号: <input type="text" formControlName="userName"/> </label> <!-- 判断如果用户名的表单无效且是在被点击或dirty状态时显示错误内容 --> <span *ngIf="userName.invalid && (userName.touched || userName.dirty)"> <span *ngIf="userName.hasError('required')">请输入用户名</span> <span *ngIf="userName.hasError('minlength') || userName.hasError('maxlength')">请输入3-5位用户名</span> </span> </fieldset> <fieldset> <label>手机号: <input type="text" formControlName="phone"/> </label> <span *ngIf="phone.invalid && (phone.touched || phone.dirty)"> 请输入手机号 </span> </fieldset> <fieldset> <label>性别: <input type="radio" formControlName="sex" value="1"/>男 <input type="radio" formControlName="sex" value="0"/>女 </label> </fieldset> <fieldset> <form formGroupName="pwd"> <label>密码: <input type="text" formControlName="password"/> </label> <label>确认密码: <input type="text" formControlName="confirmPassword"/> </label> </form> </fieldset> <button type="submit">提交</button> <button (click)="updateFormData()">更新表单</button> </form>
6.提示信息的优化处理
-
可以使用表单字段的error对象获取对应的错误信息内容。当校验都通过时返回null,required条件没通过时返回
{'required':true}
,当length条件没通过时返回{'maxlength':{'requiredLength':5,'actualLength':实际字段长度}}
-
可以通过修改FormControl的第二个参数值为AbstractControlOptions类型定义对应的校验规则,在对象中可以通过validators设置校验器类型,通过updateOn设置什么时间触发校验
-
TypeScript代码
import { Component, OnInit } from '@angular/core'; import { FormGroup, FormControl, Validators, AbstractControl } from '@angular/forms'; @Component({ selector: 'app-FormGroupTest', templateUrl: './FormGroupTest.component.html', styleUrls: ['./FormGroupTest.component.css'] }) export class FormGroupTestComponent { userInfo = new FormGroup({ userName: new FormControl('', [Validators.required, Validators.minLength(3), Validators.maxLength(5)]), // 可以设置第二个参数为一个AbstractControlOptions类型 // 在该对象类型中可以传递validators / asyncValidators / updateOn // 其中updateOn表示当什么时间触发校验 phone: new FormControl('', {validators: [Validators.required, Validators.pattern(/^1[34578]\d{9}$/)], updateOn:'blur'}), sex: new FormControl('1', [Validators.required]), pwd: new FormGroup({ password: new FormControl(), confirmPassword: new FormControl() }) }); onSubmit() { console.log(this.userInfo.value); } updateFormData() { // this.userInfo.patchValue({'userName':'Jack'}); this.userInfo.get('userName').setValue('Jerry'); } get userName(): AbstractControl { return this.userInfo.get('userName'); } get phone(): AbstractControl { return this.userInfo.get('phone'); } }
-
HTML代码
<form [formGroup]="userInfo" (ngSubmit)="onSubmit()"> <fieldset> <label>账号: <input type="text" formControlName="userName"/> </label> <!-- 判断如果用户名的表单无效且是在被点击或dirty状态时显示错误内容 --> <span *ngIf="userName.invalid && (userName.touched || userName.dirty)"> <!-- 使用errors对象可以获取到对应的错误内容 --> <span *ngIf="userName.errors.required">请输入用户名</span> <span *ngIf="userName.errors.minlength">用户名的长度不能小于{{userName.errors.minlength.requiredLength}}个字符</span> <span *ngIf="userName.errors.maxlength">用户名的长度不能超过{{userName.errors.maxlength.requiredLength}}个字符</span> <!-- 检验都通过返回null 当required条件没通过时返回的是: {'required': true} 当length条件没通过时返回的是: {'maxlength': {'requiredLength':5, 'actualLength':6}} --> </span> </fieldset> <fieldset> <label>手机号: <input type="text" formControlName="phone"/> </label> <span *ngIf="phone.invalid && (phone.touched || phone.dirty)"> 请输入手机号 </span> </fieldset> <fieldset> <label>性别: <input type="radio" formControlName="sex" value="1"/>男 <input type="radio" formControlName="sex" value="0"/>女 </label> </fieldset> <fieldset> <form formGroupName="pwd"> <label>密码: <input type="text" formControlName="password"/> </label> <label>确认密码: <input type="text" formControlName="confirmPassword"/> </label> </form> </fieldset> <button type="submit">提交</button> <button (click)="updateFormData()">更新表单</button> </form>
7.自定义表单规则
-
查看源码可以发现定义表单规则函数参数是AbstractControl类型对象,返回是ValidationErrors类型对象,即具体的错误提示对象
-
使用时可以直接使用对应的表单校验规则函数
-
如果是FormGroup嵌套FormGroup的类型,则可以在get方法中参数使用
子FormGroup.字段类型
的方式获取对应的属性值 -
错误内容显示在页面即可以使用errors类型对象的自定义错误键获取错误提示内容
-
校验器定义代码
import { AbstractControl, ValidationErrors } from "@angular/forms"; export function passwordValidator (control: AbstractControl) : ValidationErrors | null { const reg = /^[A-Z][\w\_\-]{2,5}$/; return reg.test(control.value) ? null : {passwordError: '密码必须以大写字母开头,只能包含数字、字母、下划线、中划线、长度在3-6之间'}; }
-
TypeScript代码
import { Component, OnInit } from '@angular/core'; import { FormGroup, FormControl, Validators, AbstractControl } from '@angular/forms'; import { passwordValidator } from './Validators'; @Component({ selector: 'app-FormGroupTest', templateUrl: './FormGroupTest.component.html', styleUrls: ['./FormGroupTest.component.css'] }) export class FormGroupTestComponent { userInfo = new FormGroup({ userName: new FormControl('', [Validators.required, Validators.minLength(3), Validators.maxLength(5)]), // 可以设置第二个参数为一个AbstractControlOptions类型 // 在该对象类型中可以传递validators / asyncValidators / updateOn // 其中updateOn表示当什么时间触发校验 phone: new FormControl('', {validators: [Validators.required, Validators.pattern(/^1[34578]\d{9}$/)], updateOn:'blur'}), sex: new FormControl('1', [Validators.required]), pwd: new FormGroup({ password: new FormControl('', passwordValidator), confirmPassword: new FormControl('', passwordValidator) }) }); onSubmit() { console.log(this.userInfo.value); } updateFormData() { // this.userInfo.patchValue({'userName':'Jack'}); this.userInfo.get('userName').setValue('Jerry'); } get userName(): AbstractControl { return this.userInfo.get('userName'); } get phone(): AbstractControl { return this.userInfo.get('phone'); } get password(): AbstractControl { return this.userInfo.get('pwd.password');//通过formGroup.字段名的方式获取具体表单字段 } get confirmPassword(): AbstractControl { return this.userInfo.get('pwd.confirmPassword'); } }
-
HTML代码
<form [formGroup]="userInfo" (ngSubmit)="onSubmit()"> <fieldset> <label>账号: <input type="text" formControlName="userName"/> </label> <!-- 判断如果用户名的表单无效且是在被点击或dirty状态时显示错误内容 --> <span *ngIf="userName.invalid && (userName.touched || userName.dirty)"> <!-- 使用errors对象可以获取到对应的错误内容 --> <span *ngIf="userName.errors.required">请输入用户名</span> <span *ngIf="userName.errors.minlength">用户名的长度不能小于{{userName.errors.minlength.requiredLength}}个字符</span> <span *ngIf="userName.errors.maxlength">用户名的长度不能超过{{userName.errors.maxlength.requiredLength}}个字符</span> <!-- 检验都通过返回null 当required条件没通过时返回的是: {'required': true} 当length条件没通过时返回的是: {'maxlength': {'requiredLength':5, 'actualLength':6}} --> </span> </fieldset> <fieldset> <label>手机号: <input type="text" formControlName="phone"/> </label> <span *ngIf="phone.invalid && (phone.touched || phone.dirty)"> 请输入手机号 </span> </fieldset> <fieldset> <label>性别: <input type="radio" formControlName="sex" value="1"/>男 <input type="radio" formControlName="sex" value="0"/>女 </label> </fieldset> <fieldset> <form formGroupName="pwd"> <label>密码: <input type="text" formControlName="password"/> </label> <!-- 直接通过返回的errors中自己定义的错误键获取即可 --> <span *ngIf="password.invalid && (password.touched || password.dirty)"> {{password.errors.passwordError}} </span> <label>确认密码: <input type="text" formControlName="confirmPassword"/> </label> <span *ngIf="confirmPassword.invalid && (confirmPassword.touched || confirmPassword.dirty)"> {{password.errors.passwordError}} </span> </form> </fieldset> <button type="submit">提交</button> <button (click)="updateFormData()">更新表单</button> </form>
8.给FormGroup添加校验规则
-
完成功能:校验密码与确认密码值是否相同,且定义在一个FormGroup中
-
定义参数是FormGroup的校验规则作为新的校验器
// 用于校验表单中密码重复输入是否相同 export function passwordEqualValidator(group: FormGroup): ValidationErrors | null { const password = group.get('password').value; const confirm = group.get('confirmPassword').value; return password === confirm ? null : {notEqual: '两次输入的密码不一致.'} }
-
将passwordEqualValidator作为FormGroup第二个参数进行赋值,并定义对应FormGroup的get方法,方便在html中进行获取
import { Component, OnInit } from '@angular/core'; import { FormGroup, FormControl, Validators, AbstractControl } from '@angular/forms'; import { passwordValidator, passwordEqualValidator } from './Validators'; @Component({ selector: 'app-FormGroupTest', templateUrl: './FormGroupTest.component.html', styleUrls: ['./FormGroupTest.component.css'] }) export class FormGroupTestComponent { userInfo = new FormGroup({ userName: new FormControl('', [Validators.required, Validators.minLength(3), Validators.maxLength(5)]), // 可以设置第二个参数为一个AbstractControlOptions类型 // 在该对象类型中可以传递validators / asyncValidators / updateOn // 其中updateOn表示当什么时间触发校验 phone: new FormControl('', {validators: [Validators.required, Validators.pattern(/^1[34578]\d{9}$/)], updateOn:'blur'}), sex: new FormControl('1', [Validators.required]), pwd: new FormGroup({ password: new FormControl('', passwordValidator), confirmPassword: new FormControl('', passwordValidator) }, passwordEqualValidator) }); onSubmit() { console.log(this.userInfo.value); } updateFormData() { // this.userInfo.patchValue({'userName':'Jack'}); this.userInfo.get('userName').setValue('Jerry'); } get userName(): AbstractControl { return this.userInfo.get('userName'); } get phone(): AbstractControl { return this.userInfo.get('phone'); } get password(): AbstractControl { return this.userInfo.get('pwd.password');//通过formGroup.字段名的方式获取具体表单字段 } get confirmPassword(): AbstractControl { return this.userInfo.get('pwd.confirmPassword'); } get pwd() { return this.userInfo.get('pwd'); } }
-
页面中应添加针对FormGroup的规则invalid判断,并通过errors对象获取对应的错误信息
<form [formGroup]="userInfo" (ngSubmit)="onSubmit()"> <fieldset> <label>账号: <input type="text" formControlName="userName"/> </label> <!-- 判断如果用户名的表单无效且是在被点击或dirty状态时显示错误内容 --> <span *ngIf="userName.invalid && (userName.touched || userName.dirty)"> <!-- 使用errors对象可以获取到对应的错误内容 --> <span *ngIf="userName.errors.required">请输入用户名</span> <span *ngIf="userName.errors.minlength">用户名的长度不能小于{{userName.errors.minlength.requiredLength}}个字符</span> <span *ngIf="userName.errors.maxlength">用户名的长度不能超过{{userName.errors.maxlength.requiredLength}}个字符</span> <!-- 检验都通过返回null 当required条件没通过时返回的是: {'required': true} 当length条件没通过时返回的是: {'maxlength': {'requiredLength':5, 'actualLength':6}} --> </span> </fieldset> <fieldset> <label>手机号: <input type="text" formControlName="phone"/> </label> <span *ngIf="phone.invalid && (phone.touched || phone.dirty)"> 请输入手机号 </span> </fieldset> <fieldset> <label>性别: <input type="radio" formControlName="sex" value="1"/>男 <input type="radio" formControlName="sex" value="0"/>女 </label> </fieldset> <fieldset> <form formGroupName="pwd"> <label>密码: <input type="text" formControlName="password"/> </label> <!-- 直接通过返回的errors中自己定义的错误键获取即可 --> <span *ngIf="password.invalid && (password.touched || password.dirty)"> {{password.errors.passwordError}} </span> <label>确认密码: <input type="text" formControlName="confirmPassword"/> </label> <span *ngIf="confirmPassword.invalid && (confirmPassword.touched || confirmPassword.dirty)"> {{confirmPassword.errors.passwordError}} </span> <span *ngIf="pwd.invalid && !password.invalid && !confirmPassword.invalid"> {{pwd.errors.notEqual}} </span> </form> </fieldset> <button type="submit">提交</button> <button (click)="updateFormData()">更新表单</button> </form>
9.动态获得表单中FormControl的值
-
动态获得FormControl的值
this.formGroupName.get('FormControlName').valueChanges.subscribe(value=>{ ... })
-
通过FormControl的setValidators方法设置Validators
this.formGroupName.get('FormControlName').setValidators([...]);
-
通过FormControl的clearValidators方法设置情况所有校验器
this.formGroupName.get('FormControlName').clearValidators();
-
通过调用FormControl的updateValueAndValidity方法更新数据值和校验器
this.formGroupName.get('FormControlName').updateValueAndValidity();
10.使用FormBuilder服务简化表单创建过程
-
在实际开发中,如果使用
new FormGroup() new FormControl()
会使代码比较笨重,可以使用Angular内置的服务FromBuilder来简化表单创建 -
FormBuilder的使用
-
在constructor中注入FormBuilder
constructor(private fb: FormBuilder)
-
可以通过
this.fb.group({}
创建FormGroup,this.fb.control()
创建FormControl,this.fb.array()
创建FormArray -
通过FormBuilder可以将代码简化成如下
userInfo = this.fb.group({ userName: ['', [Validators.required, Validators.minLength(3), Validators.maxLength(5)]], phone: ['', {validators: [Validators.required, Validators.pattern(/^1[34578]\d{9}$/)], updateOn:'blur'}], ... });
-