1.模板式表单
- 模板式表单使用ngForm、ngModel、ngModelGroup来完成,它们是FormsModule里的内容,所以在使用模板式表单时需要在app.module.ts中引入FormsModule。
- ngForm会自动拦截标准的表单处理事件,angular用ngSubmit代替标准的表单提交ngForm隐式的创建一个FormGroup类的实例,代表表单的数据模型,用来存储表单数据,标有这个指令的表单会自动发现标有ngModel指令的表单子元素,并将它们的值添加到数据模型中。
- ngForm相当于会创建一个实例对象,用于存储需要的表单值
- ngModel相当于一个标识,表明该元素的值为该表单的一个属性,需要为该元素提供‘name’属性,否则会报错
- ngModelGroup相当于在表单对象里再创建一个对象,存储表单中一些组合在一起使用的值
- 使用ngSubmit事件绑定提交事件(用submit也行,目前我还不知道区别在哪,知道再来补充,知道的也可以留言告知一下我,谢谢^-^)
- 因为模板式表单的所有操作都是在界面完成,所以提交时需要传入提交的值(模板本地变量名.value)
- ngForm、ngModel、ngModelGroup开头字母都要使用小写,否则会报错
- 模板式表单的使用案列
2.响应式表单
- 响应式表单指令
- formGroup类用于创建响应式表单,是多个formControl的集合,用于存储表单值
- formControl类用于声明表单字段
- formArray类用于声明一个数组字段
- 响应式表单的使用案列
- 先在ts文件中创建formGRoup,以及设置表单字段等信息
- 引入formBuilder可更方便快捷的创建响应式表单
- FormArray与FormGroup类似,但是它有一个额外的长度属性,FormGroup代表表单的固定子集,而FormArray代表一个可以增长的集合
- FormArray中的FormControl是没有相关的key,只能通过下标来访问
- formBulider可以接受一个参数,eg. 第一个参数是formControl的初始值,第二个是校验方法,第三个是异步校验方法
- formArray增加formControl
- formArray删除formControl
3.响应式表单验证
- 内置表单验证
- 常用required,minlength,maxlength
- 单个验证用法(数组的第一个值为formControl的默认值,第二个值为formControl的同步校验方法,第三个值为formControl的异步校验方法)
validRequired: ['', Validators.required], //必填项 validMinlength: ['', Validators.minLength(3)], //长度最少为3 validMaxlength: ['', Validators.maxLength(6)], //长度最大为6
-
多个验证用法(把多个同步验证的方法放到一个数组内,只要有一个不符合,整个formControl就是无效错误的)
validMix: ['', [Validators.required, Validators.minLength(3), Validators.maxLength(6)]]
- 单个验证用法(数组的第一个值为formControl的默认值,第二个值为formControl的同步校验方法,第三个值为formControl的异步校验方法)
- 常用required,minlength,maxlength
- 自定义验证方法
- 单个formControl的验证方法
-
formGroup的验证方法
-
异步验证方法(用于向服务器请求,返回的是一个Observable观察者对象)
import { FormControl, FormGroup } from '@angular/forms'; import { Observable } from 'rxjs'; import 'rxjs/add/observable/of'; // 单个formControl的验证方法 export function mobileValidator(control: FormControl): any { const config = /^(((13[0-9]{1})|(15[0-9]{1})|(18[0-9]{1}))+\d{8})$/; let res = config.test(control.value); //mobile作为一个key作为错误信息的一个关键词来使用,true可以为任意其他值,没有任何影响 // return res ? null : { mobile: 66 } true改为66,hdjsah或者其他任意类型的值也是可以的,没有什么影响 return res ? null : { mobile: true } } // formGroup的验证方法 export function equalValidator(group: FormGroup): any { const validCombination1 = group.get('validCombination1') as FormControl; const validCombination2 = group.get('validCombination2') as FormControl; let res = (validCombination1.value === validCombination2.value); return res ? null : { equal: true } } // 异步验证方法 export function mobileAsyncValidator(control: FormControl): Observable<any> { const config = /^(((13[0-9]{1})|(15[0-9]{1})|(18[0-9]{1})) + \d{8})$/; let res = config.test(control.value); return Observable.of(res ? null : { mobileAsync: true }); }
- 响应式表单验证的使用案列
- 声明响应式表单
formModel: FormGroup; constructor(formBuilder: FormBuilder) { this.formModel = formBuilder.group({ validRequired: ['', Validators.required], //必填项 validMinlength: ['', Validators.minLength(3)], //长度最少为3 validMaxlength: ['', Validators.maxLength(6)], //长度最大为6 validMix: ['', [Validators.required, Validators.minLength(3), Validators.maxLength(6)]], //多个验证方法 validCustom: ['', [Validators.required, mobileValidator]], //内置验证方法 + 自定义验证方法 validCustomAsync: ['', Validators.required,mobileAsyncValidator], //内置验证方法 + 自定义异步验证方法 validCombination: formBuilder.group({ validCombination1: [''], validCombination2: [''] }, { validator: equalValidator }) //formGroup验证的使用,验证方法为对象,validator : 方法名 }) }
- 错误信息hasError
<!-- 内置验证方法,使用关键字required,minlength,maxlength(小写) --> [hidden] = "!formModel.hasError('required', 'username')" <!-- 自定义验证方法,使用定义时返回的关键字mobile --> [hidden] = "!formModel.hasError('mobile', 'mobile')"
-
状态字段(touched、untouched、pristine、dirty、pending)
touched、untouched 判断是否获取过焦点 pristine(从未被改变过,值为true,反之....)、dirty(被修改过,值为true) 判断字段的值有没有被改变过 pending 正处于异步校验时这个属性为true
-
界面提示错误信息案列
<form [formGroup]="formModel" (ngSubmit)="onSubmit()"> <div> required验证 :<input formControlName="validRequired" type="text"> <span class="error" [hidden]="!formModel.hasError('required', 'validRequired') ||formModel.get('validRequired').untouched"> 该项为必填项! </span> </div> <div> minlength验证 :<input formControlName="validMinlength" type="text"> <span class="error" [hidden]="!formModel.hasError('minlength', 'validMinlength')|| formModel.get('validMinlength').untouched"> 长度至少为3位! </span> </div> <div> maxlength验证 :<input formControlName="validMaxlength" type="text"> <span class="error" [hidden]="!formModel.hasError('maxlength', 'validMaxlength')|| formModel.get('validMaxlength').untouched"> 长度最多为6位! </span> </div> <div> 混合验证 : <input formControlName="validMix" type="text"> <span [hidden]="formModel.get('validMix').untouched"> <span class="error" [hidden]="!formModel.hasError('required', 'validMix')"> 该项为必填项! </span> <span class="error" [hidden]="!formModel.hasError('minlength', 'validMix')"> 长度至少为3位! </span> <span class="error" [hidden]="!formModel.hasError('maxlength', 'validMix')"> 长度最多为6位! </span> </span> </div> <div> 自定义验证 : <input formControlName="validCustom" type="text"> <span class="error" [hidden]="!formModel.hasError('required', 'validCustom')|| formModel.get('validCustom').untouched"> 该项为必填项! </span> <span class="error" [hidden]="formModel.hasError('required', 'validCustom')|| !formModel.hasError('mobile1', 'validCustom')|| formModel.get('validCustom').pristine"> 请填写正确的手机号码! </span> </div> <div> 自定义异步验证 : <input formControlName="validCustomAsync" type="text"> <span class="error" [hidden]="!formModel.hasError('required', 'validCustomAsync') ||formModel.get('validCustomAsync').untouched"> 该项为必填项! </span> <span class="error" [hidden]="formModel.hasError('required', 'validCustomAsync')|| !formModel.hasError('mobileAsync', 'validCustomAsync')|| formModel.get('validCustomAsync').pristine"> 请填写正确的手机号码! </span> </div> <div formGroupName="validCombination"> <div> 组合验证1 :<input formControlName="validCombination1" type="password"> </div> <div> 组合验证2 :<input formControlName="validCombination2" type="password"> <span class="error" [hidden]="!formModel.hasError('equal','validCombination')|| formModel.get(['validCombination','validCombination1']).pristine|| formModel.get(['validCombination','validCombination2']).pristine"> 两次输入值不相同! </span> </div> </div> <div> <button type="submit">提交</button> </div> </form>
- 声明响应式表单
4.模板式表单验证
- 模板式表单因为只能在html界面操作,所以需要创建指令来进行表单验证(使用‘ ng g d 指令名 ’ 创建指令)
- 编辑指令(以mobile验证方法为例子)
import { Directive } from '@angular/core'; import { NG_VALIDATORS } from '@angular/forms'; import { mobileValidator } from './my-validators/my-validator'; @Directive({ selector: '[mobile]', //属性名 providers: [{ provide: NG_VALIDATORS, //验证指令必须需要的服务 useValue: mobileValidator, //验证方法 multi: true // 是否允许同一个元素拥有多个属性,true为允许,false为不允许。如 : ngfor与ngif指令,该指令的multi为false, // 所以在一个html元素中,不允许同时出现ngif和ngfor,如<div *ngIf="a" *ngFor="let a in b"></div>是会报错的 }] }) export class MobileValidatorDirective { constructor() { } }
- 在html中作为属性来使用(在form标签中使用novalidate来阻止默认的验证方法,防止出现冲突错误)
<form #myForm="ngForm" (submit)="onSubmit(myForm.value)" autocomplete="off" novalidate> <div> 用户名:<input required minlength="4" ngModel name="username" type="text" (input)="onUserNameInput(myForm)"> <span [hidden]="userNameValid || userNametouched"> <span class="error" [hidden]="!myForm.form.hasError('required', 'username')"> 该项为必填项! </span> <span class="error" [hidden]="!myForm.form.hasError('minlength', 'username')"> 用户名长度至少4位! </span> </span> </div> <div> <!-- mobile属性就是自定义的验证指令 --> 手机号:<input ngModel required mobile name="mobile" type="tel"> <span class="error" [hidden]="!myForm.form.hasError('required', 'mobile')"> 该项为必填项! </span> <span class="error" [hidden]="myForm.form.hasError('required', 'mobile')|| !myForm.form.hasError('mobile', 'mobile')"> 请填写正确手机号! </span> </div> <div ngModelGroup='passwordGroup' equal> <div> 密码:<input ngModel required name="password" type="password"> </div> <div> 确认密码:<input ngModel required name="pconfirm" type="password"> </div> <span class="error" [hidden]="!myForm.form.hasError('equal', 'passwordGroup')"> 密码不一致! </span> </div> <button type="submit">提交</button> </form>
- 错误信息(与响应式差别在于响应式使用的是formModel.hasError,模板式使用的是myForm.form.hasError)
<span class="error" [hidden]="!myForm.form.hasError('mobile', 'mobile')"> 请填写正确的手机号! </span>
- 状态字段(模板式表单只能设置相对应的方法来设置状态,所以不适合做复杂场景的表单)
<div> 用户名:<input required minlength="4" ngModel name="username" type="text" (input)="onUserNameInput(myForm)"> <span [hidden]="userNameValid || userNametouched"> <span class="error" [hidden]="!myForm.form.hasError('required', 'username')"> 该项为必填项! </span> <span class="error" [hidden]="!myForm.form.hasError('minlength', 'username')"> 用户名长度至少4位! </span> </span> </div>
userNameValid: boolean = true; userNameUntouched: boolean = true; onUserNameInput(form: NgForm) { if (form) { this.userNameValid = form.form.get('username').valid; this.userNameUntouched = form.form.get('username').untouched; } }
- formGroup的验证方法与响应式的验证方法有些不同
// 响应式formGroup的验证方法 export function equalValidator(group: FormGroup): any { const validCombination1 = group.get('validCombination1') as FormControl; const validCombination2 = group.get('validCombination2') as FormControl; let res = (validCombination1.value === validCombination2.value); return res ? null : { equal: true } } // 模板式formGroup的验证方法 export function equalTempValidator(group: FormGroup): any { const password = group.value.password as FormControl; const pconfirm = group.value.pconfirm as FormControl; let res = (password === pconfirm); return res ? null : { equal: true } }
5.更新在线竞拍搜索表单
- 创建响应式表单
import { Component, OnInit } from '@angular/core'; import { ProductService } from 'src/shared/product.service'; import { FormGroup, FormBuilder, Validators, FormControl } from '@angular/forms'; @Component({ selector: 'app-search', templateUrl: './search.component.html', styleUrls: ['./search.component.css'] }) export class SearchComponent implements OnInit { private types: string[]; formModel: FormGroup; constructor(private _ProductService: ProductService, FormBuilder: FormBuilder) { this.formModel = FormBuilder.group({ productName: ['', Validators.minLength(2)], productPrice: [null, priceNumberValidator], productType: [-1] }) } ngOnInit() { this.types = this._ProductService.getAllType(); } onSubmit() { console.log(this.formModel.value); } }
- 创建自定义校验方法(本次放在search组件的ts中,也可以定义一个文件夹存放自定义验证方法)
export function priceNumberValidator(control: FormControl): any { let res = (control.value > 0); return res ? null : { number: true }; }
- 修改html代码,添加错误提示信息
<form [formGroup]="formModel" (ngSubmit)="onSubmit()"> <div class="form-group"> <label for="">商品名称</label> <input formControlName="productName" type="text" class="form-control" placeholder="商品名称"> <span [hidden]="formModel.get('productName').untouched"> <span class="text-danger" style="font-size:smaller" [hidden]="!formModel.hasError('minlength','productName')"> *商品名称长度至少为2位! </span> </span> </div> <div class="form-group"> <label for="">商品价格</label> <input formControlName="productPrice" type="text" class="form-control" placeholder="商品价格"> <span [hidden]="formModel.get('productPrice').untouched"> <span class="text-danger" style="font-size:smaller" [hidden]="!formModel.hasError('number','productPrice')"> *商品价格为正数! </span> </span> </div> <div class="form-group"> <label for="">商品类别</label> <select formControlName="productType" class="form-control"> <option value="-1" selected>全部</option> <option *ngFor="let type of types" value="{{type}}">{{type}}</option> </select> </div> <button type="submit" class="btn btn-primary btn-block">搜索</button> </form>