angular4 表单
1.模板式表单
在html模板上控制
指令对应的实例
1.ngForm指令:标记表单,会隐式创建FormGroup类的实例,这个类代表表单数据模型,并存储表单数据;
2.ngModel指令:标记单个字段,ngFrom指令会发现含有ngModel指令的标签,并添加到表单数据模型中,使用该指令必须跟随name属性;
3.ngModelGroup指令:标记多个字段组合,表现形式是一个对象,如{name:"张三",pass:"123"};
例子:
<!--模板变量myForm指向ngForm对象--> <form #myForm = "ngForm" (ngSubmit) = "onSubmit(myForm.value)"> <div>用户名:<input ngModel type="text" name="username"/></div> <!--ngModelGroup将字段嵌套成一个组--> <div ngModelGroup="passwordGroup"> <div>密码:<input ngModel type="password" name="password"/></div> <div>确认密码:<input ngModel type="password" name="confirmPass"/></div> </div> <div>电话:<input ngModel type="text" name="mobile"/></div> <div><input type="submit" value="提交"/></div> </form>
结果:
2.响应式表单
在ts上控制表单数据,可用于动态生成表单
类对应的指令
formGroup:标记组,表单也属于组,属于属性指令,需要加 [ ]
formGroupName:标记组,用于formGroup指令范围内部。
formControl:标记字段,属于属性指令,需要加 [ ],用于在formGroup指令范围外部。
formControlName:标记字段,用于formGroup指令范围内部。
formArrayName:标记数组,用于formGroup指令范围内部。
例子:
reactiveForm.html
<form [formGroup]="formModel" (submit)=onSubmit() novalidate> <div formGroupName="dateRange"> 起始日期:<input type="date" formControlName="from"> 截止日期:<input type="date" formControlName="to"> </div> <div> <ul formArrayName="emails"> <!-- 循环formArray数组,和下标 --> <li *ngFor="let e of this.formModel.get('emails').controls; let i=index;"> <!-- formArray没有key,所有使用属性绑定[],值为数组的下标 --> <input type="text" [formControlName]="i"> </li> </ul> <button type="button" (click)="addEmail()">增加Email</button> </div> <button type="submit">保存</button> </form> <input type="text" [formControl]="username">
reactiveForm.ts
import { Component, OnInit } from '@angular/core'; import { FormControl, FormGroup, FormArray } from '@angular/forms/src/model'; @Component({ selector: 'app-reactive-form', templateUrl: './reactive-form.component.html', styleUrls: ['./reactive-form.component.css'] }) export class ReactiveFormComponent implements OnInit { //定义数据模型 formModel : FormGroup = new FormGroup({ dateRange : new FormGroup({ from:new FormControl(), to:new FormControl() }), emails : new FormArray([ new FormControl("a@aa.com"), new FormControl("b@bb.com"), ]) }); username:FormControl=new FormControl("张三"); constructor() {} ngOnInit() {} onSubmit(){ console.log(this.formModel.value); } addEmail(){ let emails=this.formModel.get("emails") as FormArray; emails.push(new FormControl()); } }
也可以使用FormBuilder类来创建数据模型,上面代码改成:
formModel : FormGroup; constructor(fb:FormBuilder){ //定义数据模型 this.formModel = fb.group({ dateRange : fb.group({ from:[''], to:[''] }), emails : fb.array([ ["a@aa.com"], ["b@bb.com"], ]) });
3.响应式表单校验
angular4提供了validator类的预定义校验:
-
required - 设置表单控件值是非空的
-
email - 设置表单控件值的格式是 email
-
minlength - 设置表单控件值的最小长度
-
maxlength - 设置表单控件值的最大长度
-
pattern - 设置表单控件的值需匹配 pattern 对应的模式
当然,也可以进行自定义校验
例子:
reactiveForm.html
<form [formGroup]="formModel" (ngSubmit) = "onSubmit()" novalidate> <div>用户名:<input formControlName="username"/></div> <!-- 当验证通过或者未点击该字段时隐藏,状态字段untouched=true表示未点击,touched=true表示已点击 --> <div [hidden]="formModel.get('username').valid || formModel.get('username').untouched"> <!-- 1.在模板上硬编码错误信息 --> <!-- [hidden]=false表示显示错误信息,hasError(校验失败返回对象的key,校验字段的name) --> <div [hidden]="!formModel.hasError('required','username')"> 用户名是必填项 </div> <div [hidden]="!formModel.hasError('minLength','username')"> 用户名最小长度6 </div> </div> <div formGroupName="passwordsGroup"> <div>密码:<input formControlName="password"/></div> <!-- 包含在group中的hasError第二个参数为数组[groupname,controlname] --> <div [hidden]="!formModel.hasError('minLength',['passwordsGroup','password'])"> 密码最小长度是6 </div> <div>确认密码:<input formControlName="pconfirm"/></div> <div [hidden]="!formModel.hasError('equal','passwordsGroup')"> {{formModel.getError('equal',passwordsGroup)?.passErrDesc}} </div> </div> <div>电话:<input formControlName="mobile"/></div> <!-- 当验证没有通过或者未改变该字段值时显示 状态字段pristine=ture表示未改变,dirty=ture表示已改变--> <div [hidden]="formModel.get('mobile').valid || formModel.get('username').pristine"> <!-- 2.动态错误提示 --> <div [hidden]="!formModel.hasError('mobile','mobile')"> {{formModel.getError('mobile')?.mobileErrDesc}} </div> </div> <div><input type="submit" value="提交"/></div> </form>
reactiveForm.ts
import { Component, OnInit } from '@angular/core'; import { FormGroup, FormControl } from '@angular/forms/src/model'; import { FormBuilder,Validators} from '@angular/forms'; @Component({ selector: 'app-reactive-form2', templateUrl: './reactive-form2.component.html', styleUrls: ['./reactive-form2.component.css'] }) export class ReactiveForm2Component implements OnInit { formModel:FormGroup; /** * 自定义手机号校验 * 返回值:正确返回 null * 错误返回 key:value(value可以是对象错误key:错误信息,也可以是任意值) * @param control */ mobileValidator(control:FormControl):any{ var myreg = /^((0\d{2,3}-\d{7,8})|(1([358][0-9]|4[579]|66|7[0135678]|9[89])[0-9]{8}))$/; let valid = myreg.test(control.value); console.log("mobile的校验结果是"+valid); return valid ? null : {mobile:{mobileErrDesc:"密码和确认密码不匹配"}}; } //自定义密码组校验 equalValidatir(group:FormGroup):any{ let password:FormControl= group.get("password") as FormControl; let pconfirm:FormControl= group.get("pconfirm") as FormControl; let valid:boolean = (password.value === pconfirm.value); console.log("密码校验结果:"+valid); return valid ? null :{equal:{passErrDesc:"密码和确认密码不匹配"}}; } constructor(fb:FormBuilder) { //定义数据模型 this.formModel = fb.group({ //预定义校验 username:['',[Validators.required,Validators.minLength(6)]], mobile:['',this.mobileValidator], passwordsGroup:fb.group({ password:[''], pconfirm:[''] },{validator:this.equalValidatir}) }); } ngOnInit() {} onSubmit(){ //整个表单校验通过 if(this.formModel.valid){ console.log(this.formModel.value); } } }
当然,有些校验方法时通用的,所以我们可以提取出全局的校验方法。
需要function声明该方法,再通过 export 关键字暴露该方法。
validator.ts
import { FormControl, FormGroup } from "@angular/forms"; //自定义手机号码组校验 export function mobileValidator(control:FormControl):any{ var myreg = /^((0\d{2,3}-\d{7,8})|(1([358][0-9]|4[579]|66|7[0135678]|9[89])[0-9]{8}))$/; let valid = myreg.test(control.value); console.log("mobile的校验结果是"+valid); return valid ? null : {mobile:{mobileErrDesc:"密码和确认密码不匹配"}}; } //自定义密码组校验 export function equalValidatir(group:FormGroup):any{ let password:FormControl= group.get("password") as FormControl; let pconfirm:FormControl= group.get("pconfirm") as FormControl; let valid:boolean = (password.value === pconfirm.value); console.log("密码校验结果:"+valid); return valid ? null :{equal:{passErrDesc:"密码和确认密码不匹配"}}; }
异步校验器
用来调用远程的服务来校验表单的值。异步校验器跟普通的校验器一样,只不过返回的是一个可观测的流。
例子:
validator.ts
import { FormControl, FormGroup } from "@angular/forms"; //自定义手机号码组校验 export function mobileAsyncValidator(control:FormControl):any{ var myreg = /^((0\d{2,3}-\d{7,8})|(1([358][0-9]|4[579]|66|7[0135678]|9[89])[0-9]{8}))$/; let valid = myreg.test(control.value); console.log("mobile的校验结果是"+valid); //Observable.of返回一个流,.delay(5000)表示延迟5秒,用来模拟服务器响应时间 return Observable.of(valid ? null : {mobile:{mobileErrDesc:"密码和确认密码不匹配"}}).delay(5000); }
reactiveForm.ts
constructor(fb:FormBuilder) { //定义数据模型 this.formModel = fb.group({ //预定义校验 username:['',[Validators.required,Validators.minLength(6)]], //异步校验作为第3个构造函数 mobile:['',this.mobileValidator,this.mobileAsyncValidator], passwordsGroup:fb.group({ password:[''], pconfirm:[''] },{validator:this.equalValidatir}) }); }
<!--状态字段pending=true表示正在进行校验--> <div [hidden]="!formModel.get('mobile').pending"> 正在校验手机合法性 </div>
4.模板式表单校验
终端输入命令$ ng g directive directive/mobileValidator 创建指令文件
将校验方法定义成指令,如下
mobileValidator.directive.ts
import { Directive } from '@angular/core'; import { NG_VALIDATORS } from '@angular/forms'; import { mobileValidator } from '../validator/validator'; //指令装饰器 @Directive({ selector: '[mobile]',//指令名称 //useValue:校验方法名称 ,multi:true:表示token为NG_VALIDATORS的提供器可对应多个useValue providers:[{provide:NG_VALIDATORS,useValue:mobileValidator,multi:true}] }) export class MobileValidatorDirective { constructor() { } }
在模板中使用指令来校验模板表单
templateForm.component.html
<!-- novalidate指令:不启用浏览器默认的表单校验,让angualr的校验指令不受干扰 --> <form #myForm = "ngForm" (ngSubmit) = "onSubmit(myForm.value,myForm.valid)" novalidate> <!-- 使用angular预定义的指令required,minlength等来校验表单 --> <div>用户名:<input ngModel required minlength="6" name="username" type="text"/></div> <div ngModelGroup="passwordGroup" equal> <div>密码:<input ngModel type="password" name="password"/></div> <div>确认密码:<input ngModel type="password" name="confirmPass"/></div> </div> <!-- 定义input事件来控制是否显示错误提示 --> <div>电话:<input ngModel mobile name="mobile" type="text" (input)="onMobileInput(myForm)"/></div> <!-- 接收模板的值判断是否显示错误提示 --> <div [hidden]="mobileValid || mobileUntouched"> <!-- 使用myForm.form获取表单对象 --> <div [hidden]="!myForm.form.hasError('mobile','mobile')"> 电话号码不正确 </div> <div> <div><input type="submit" value="提交"/></div> </form>
templateForm.component.ts
import { Component, OnInit } from '@angular/core'; import { NgForm } from '@angular/forms/src/directives/ng_form'; @Component({ selector: 'app-template-form', templateUrl: './template-form.component.html', styleUrls: ['./template-form.component.css'] }) export class TemplateFormComponent implements OnInit { constructor() { } ngOnInit() {} mobileValid:boolean=true; mobileUntouched:boolean=true; //点击电话输入框时,判断mobile是否合法 onMobileInput(form:NgForm){ if(form){ this.mobileValid = form.form.get("mobile").valid; this.mobileUntouched = form.form.get("mobile").untouched; } } onSubmit(value:any,valid:boolean){ console.log(valid); console.log(value); } }