第七章 表单处理
学习内容:
模板式表单
响应式表单
表单验证
一.简述模板式表单与响应式表单的不同
Angular表单API
模板式表单简述:
表单数据模型通过组件模板中的相关指令定义,但是只
适用于一些简单的场景。比较死板
响应式表单简述:
通过typescript创建底层的数据模型。可通过特定的
指令将模板上的HTML元素和底层的数据模型相联系。比较灵活
两种表单在数据模型中的异同:
1.都需要数据模型来存储表单数据。
2.数据模型由angular/forms模块中的一些特定的类,
如FormControl,FormGroup等组成。
3.模板式表单中,数据模型由组件模板中的指令隐式创建。
响应式表单中,数据模型自己来创建,并与HTML元素相连接。
4.模板式表单中,不能访问这些类;而响应式可以。
模块引入的不同
1.模板式表单只需引入FormsModule模块
2.使用响应式,必须引入 FormsModule、
ReactiveFormsModule这两个模块。
二. 模板式表单
指令:来自FormsModule模块中
ngForm 作用:
1.ngForm指令可以发现所有标有 ‘ngModel’的子元素,并将其值添加到表单模型中
创建FormGroup的实例,将数据存储在ngForm.value对象中。
2.任何标有“ngForm”指令的元素都会有此作用。不论是不是form表单元素
3.当不希望Angular接管表单时,可给表单标签添加“ngNoForm”指令。
4.ngForm指令可以被模板本地变量引用,来访问模板表单的实例
5.会阻止表单的提交并刷新,使用**ngSubmit**事件来提交
6.隐式创建FormGroup类型的实例
ngModel 作用:
1.ngModel是ngForm指令所在元素的子元素的字段,
与name属性相关联(仅当添加name属性后才能被模板捕获,创建数据模型)。
2.会隐式创建FormControl的实例,来代表该字段,并用FormControl创建的对象存储其值。
3.ngModel代表一个字段,而不是双向绑定,不需要绑定变量,也不需要在组件中声明变量。
4.ngModel指令可以被模板本地变量引用,来访问模板表单的字段的值。
ngFormGroup 作用:
1.与ngForm类似,也会创建FormGroup的实例,该数据是嵌套在ngForm.value对象中的。
2.用来将有联系的表单元素放置于一块,并组成对象格式数据。
代码示例:
<form #myForm="ngForm" (ngSubmit)="createUser(myForm.value)"> // #myForm="ngForm" 模板本地变量引入ngForm指令
<div>姓名:<input type="text" #myName="ngModel" ngModel name="myname"></div>
<div>邮箱:<input type="email" ngModel name="email"></div>
<div>手机号:<input type="number" ngModel name="mobile"></div>
<div ngModelGroup="passwordInfo">
<div>密码:<input type="password" ngModel name="password"></div>
<div>确认密码:<input type="password" ngModel name="passwordConfirm"></div>
</div>
<button tyoe="submit">注册</button>
</form>
<div>
{{myForm.value | json}} // 模板本地变量引入ngForm
</div>
<div>
{{myName.value | json}} // 模板本地变量引入ngModel
</div>
createUser(info: any) {
console.log(info);
}
展示数据:
{
"myname": "hello",
"email": "19242924@qq.com",
"mobile": 13479347937,
"passwordInfo": { "password": "12121", "passwordConfirm": "121212" }
}
hello
三. 响应式表单:
1.编写响应式表单步骤:
(1)用代码编写数据模型
(2)将数据模型与HTML页面ngModel相连接
2.数据模型:
(1)数据模型:用来保存表单数据的数据结构,简称模型。由FormsModule中的三个类
组成:FormControl、FormGroup、FormArray.
FormControl指代form中的单个字段
FormGroup指代form中的所有字段或者有关联的字段,类型为对象,可嵌套
FormArray指代form中可能有多个值的字段,类型为数组
(2)例子:创建响应式表单中,不同类型的数据:
private formModel: FormGroup;
constructor() {
this.formModel = new FormGroup({ // 整个表单是FormGroup类型的数据
nickname: new FormControl(), // 昵称为FormControl类型的数据
mobile: new FormControl(),
emails: new FormArray([ // emails 是FormArray类型的数据
new FormControl('a@a.com'),
new FormControl('b@b.com')
]),
passwordInfo: new FormGroup({ // 密码是FormGroup类型的数据,被嵌套在FormGroup中
password: new FormControl(),
passwordConfirm: new FormControl()
})
});
}
(3)注意情况:
1.响应式表单的指令: 来自ReactiveFormModule模块中
2.属性绑定式指令: 使用属性绑定的方式进行绑定
formGroup、formControl
用法:只有当创建了对应的变量时,才能使用:
private formModel: FormGroup;
3.name绑定式指令:直接绑定字段的名称即可
formGroupName、formControlName、formArrayName
4.响应式表单指令是不能创建本地变量来引用。
5.创建响应式表单数据结构:
export class ReactiveFormComponent implements OnInit {
private xxx: FormControl;
private formModel: FormGroup; // 声明变量;
constructor() { //创建表单数据结构
this.formModel = new FormGroup({ // 表单对象
nickname: new FormControl(),
mobile: new FormControl(),
emails: new FormArray([ // 数组
new FormControl('a@a.com'),
new FormControl('b@b.com')
]),
passwordInfo: new FormGroup({ // 对象
password: new FormControl(),
passwordConfirm: new FormControl()
})
});
}
6.绑定方法
addEmail() {
let emails = this.formModel.get('emails') as FormArray;
emails.push(new FormControl());
console.log(this.formModel.value);
}
createUser() {
console.log(this.formModel.value);
}
7.响应式表单模板
<input [formControl]="xxx"></input> // 表单外字段可使用formControl属性方式绑定
<form [formGroup]="formModel" (submit)="createUser()"> // 绑定表单
<div>姓名:<input type="text" formControlName="nickname"></div> // 绑定单个字段名称
<div>邮箱:
<ul formArrayName="emails"> // 绑定数组类数据
<li *ngFor="let email of formModel.get('emails').controls;let i = index;">
<input [formControlName]="i"> // 数组没有key值,且i为变量,所以采用属性绑定写法
</li>
</ul>
<button tyoe="button" (click)="addEmail()">增家email</button>
<!--<input type="email" formControlName="email">-->
</div>
<div>手机号:<input type="number" formControlName="mobile"></div>
<div formGroupName="passwordInfo"> // 绑定相关联的字段组合
<div>密码:<input type="password" formControlName="password"></div>
<div>确认密码:<input type="password" formControlName="passwordConfirm"></div>
</div>
<button tyoe="submit">注册</button>
</form>
8.响应式表单数据创建简化: FormBuild数据类型
private formModel: FormGroup;
private fb: FormBuilder = new FormBuilder();
constructor() {
this.formModel = this.fb.group({
nickname: ['hello'],
mobile: [''],
emails: this.fb.array([
['a@a.com'],
['b@b.com']
]),
passwordInfo: this.fb.group({
password: [''],
passwordConfirm: ['']
})
});
}
用this.fb.group({})代替new FormGroup({})、
用this.fb.array代替 new FormArray({})、
用['hello']代替new FormControl()
四.表单校验
默认angular表单校验
nickname: ['xxx', [Validators.required, Validators.minLength(6)]],
// 提交时打印校验结果
let nicknameValid: boolean = this.formModel.get('nickname').valid;
console.log(nicknameValid);
let nicknamErrors: any = this.formModel.get('nickname').errors;
console.log(nicknamErrors);
自定义表单校验
mobileValidator(mobile: FormControl): any {
let value = (mobile.value || '') + '';
let myreg = /^(((15[0-9]{1})|(13[0-9]{1})|(18[0-9]{1}))+\d{8})$/;
let valid = myreg.test(value);
console.log(valid);
return valid? null: {mobile: true};
}
passwordValidator(info: FormGroup): any {
let password: FormControl = info.get('password') as FormControl; // 获取其值
let pConrfirm: FormControl = info.get('passwordConfirm') as FormControl;
let valid = password.value === pConrfirm.value;
console.log(valid);
return valid? null: {password: true};
}
// 使用:
mobile: ['', [this.mobileValidator]], // FormControl数据模型格式为数组格式
passwordInfo: this.fb.group({
password: [''],
passwordConfirm: ['']
}, {validator: this.passwordValidator}) // FormGroup对象格式
整个表单的校验
单个数据有自己的校验,整个表单数据也有一个整体的校验:
this.formModel.valid
当返回true时,证明整个表单都验证合法。
添加校验提示
1. 模板添加校验提示
<div [hidden]="!formModel.hasError('required','nickname')" class="error">
昵称是必填项
</div>
<div [hidden]="!formModel.hasError('mobile','mobile')" class="error">
手机号格式不正确
</div>
formModel.hasError 方法传入两个参数,第一个为验证条件,第二个为数据名称
当数据为嵌套中的数据时,校验规则为:(注意数据传入方式)
<div [hidden]="!formModel.hasError('required','passwordInfo.password')" class="error">
密码是必填项
</div>
2. 自定义的校验方法返回校验提示:
- 自定义校验方法:
export function passwordValidator(info: FormGroup): any {
let password: FormControl = info.get('password') as FormControl;
let pConrfirm: FormControl = info.get('passwordConfirm') as FormControl;
let valid: boolean = password.value === pConrfirm.value;
console.log(valid);
return valid ? null : {password: {description: '密码与确认密码不匹配'}}; // 返回一个对象
}
- 展示校验内容:
<div [hidden]="!formModel.hasError('password','passwordInfo')" class="error">
{{formModel.getError('password','passwordInfo')?.description}}
</div>
3. 添加异步校验:
- 自定义校验方法:
export function mobileAsyncValidator(mobile: FormControl): any {
let value = (mobile.value || '') + '';
let myreg = /^(((15[0-9]{1})|(13[0-9]{1})|(18[0-9]{1}))+\d{8})$/;
let valid = myreg.test(value);
console.log(valid);
**return Observable.of(valid ? null : {mobile: true}).delay(5000);**
}
- 引用:
.ts文件:
mobile: ['', mobileValidator, mobileAsyncValidator]
传入的三个参数: 默认值,本地校验,异步校验
当本地校验通过后,才进行异步校验
- 模板展示:
<div>
{{formModel.status}}
</div>
五.状态字段(辅助数据校验)
1. touched 和 untouched
<div [hidden]="formModel.get('passwordInfo.password').valid || formModel.get('passwordInfo.password').untouched">
<div [hidden]="!formModel.hasError('required','passwordInfo.password')" class="error">
密码是必填项
</div>
</div>
当数据验证合法或者没有被触碰时,隐藏提示内容
2. pristine 和 dirty
数据是否发生改变
<div [hidden]="formModel.get('mobile').valid || formModel.get('mobile').pristine">
<div [hidden]="!formModel.hasError('mobile','mobile')" class="error">
手机号格式不正确
</div>
</div>
当数据合法或者没有被触碰时,隐藏提示内容
3. pending
是否进行远程异步校验时
<div [hidden]="!formModel.get('mobile').pending">
正在校验手机号合法性
</div>
动态添加类名:
[class.hasError]="formModel.get('nickname').invalid"
模板式表单的验证提示:
自定义指令:
建立指令文件的 指令: ng g directive 指令名
校验指令文件:
import {Directive} from '@angular/core';
import {NG_VALIDATORS} from "@angular/forms";
import {mobileValidator} from "../validators/Validators";
@Directive({
selector: '[mobile]', // 指令名称,可编辑
providers: [{provide: NG_VALIDATORS, useValue: mobileValidator, multi: true}]
// NG_VALIDATORS 校验所用模块,mobileValidator:校验方法
})
export class MobileValidatorDirective {
constructor() {
}
}
- 模板使用angular默认校验指令的校验提示:
// 手机号验证
<div [hidden]="!myForm.form.hasError('mobile','mobile')" class="error">
手机号格式不正确
</div>
// 密码验证:
<div [hidden]="!myForm.form.hasError('required','passwordInfo.password')" class="error">
密码是必填项
</div>
<div [hidden]="!myForm.form.hasError('password','passwordInfo')" class="error">
{{myForm.form.getError('password','passwordInfo')?.description}}
</div> // 其中.hasError(),.getError(),中的传参第一个参数是印证错误时,返回的错误的字段。
与响应式不同的是: 不能使用表单数据模型,只能使用myForm.form代替
- 模板式表单不能使用状态值(touched 、pristine....),需要使用其他方法代替:
绑定input方法
<div>手机号: <input type="number" ngModel mobile name="mobile" (input)="onMobileInpue(myForm)"></div>
onMobileInpue(form: NgForm) {
if (form) {
this.mobileValid = form.form.get('mobile').valid;
this.mobilePristine = form.form.get('mobile').pristine;
}
}
间接获取 状态值pristine
<div [hidden]="mobileValid || mobilePristine">
<div [hidden]="!myForm.form.hasError('mobile','mobile')" class="error">
手机号格式不正确
</div>
</div>
综上: 模板式表单如果需要更具体的数据检测,则会更加麻烦,
所以模板式表单只是个简单的表单创建.