标准HTML表单
- 相关检测属性:required、partern、min、max
- 一般写法
1 2 3
| <form action="/register" method="post"> <input type="text"/> </form>
|
在Angular中,有两种形式的表单:响应式表单、模板式表单
模板式表单:表单的数据模型是通过组件中的指令隐式创建的。使用这种方式定义表单的数据模型时,受限于HTML语法,模板驱动方式仅适用于一些简单的场景。
响应式表单:通过编写ts代码而不是HTML来创建底层数据模型。在模型创建之后,可以使用一些特定的指令将模板的HTML元素与底层数据模型连接在一起。
模板式表单
在Angular中,标准表单会被自动托管,常规的操作无效。如果希望使用原始表单,只需要在form标签内加上ngNoForm指令即可;
1
| <form action="/register" method="post" ngNoForm></form>
|
知识梳理
#myForm=”ngForm”获取表单对象,myForm.value获取表单的值,取值形式:
(ngSubmit)=”onSubmit(myform.value)”代替了默认的submit
只能在HTML中操作数据模型,不能在ts中操作
在ngForm上使用双向绑定时,不需要像以往那样[(ngModel)]=”user.name”,改写为:直接在标签中加入ngModel指令,同时指定name属性:name=”username”
如果要在ngmodel上使用组件模板变量,#username=”ngModel”,取值形式:
ngForm=>隐式创建FormGroup
ngModel=>隐式创建FormControl
ngModelGroup=>隐式创建FormGroup(嵌入结构,用于将一些字段更好地组织在一起,加入指定的对象中)
模板式表单的写法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <form #myForm="ngForm" (ngSubmit)="onSubmit(myForm.value)"> <div> 姓名 <input type="text" #username="ngModel" ngModel name="username"/> </div> <div ngModelGroup="psw"> <div> 密码 <input type="text" #psw_1="ngModel" ngModel name="psw_1"/> </div> <div> 密码 <input type="text" #psw_2="ngModel" ngModel name="psw_2"/> </div> </div> <button type="submit">提交</button> </form>
|
1 2 3
| onSubmit(val:any):void{ console.log(val); //查看表单的值 }
|
响应式表单
响应式表单的写法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| //注意(submit)和(ngSubmit)写法的区别 <form [formGroup]="formModel" (submit)="onSubmit()"> //通过formControlName将底层数据模型和HTML元素绑定在一起 //formControl在FormGroup中的写法 <input type="text" formControlName="username"> //使用formGroupName将特定的属性存储在指定(dateRange)的对象中 <div formGroupName="dateRange"> //FormGroup的实例对象通过key(from、to)检索元素 开始日期:<input type="date" formControlName="from"/> 结束日期:<input type="date" formControlName="to"/> </div> <div> //绑定到FormArray的实例对象 <ul formArrayName="emails"> //由于FormArray是可变长度,一般与ngFor搭配使用 //this.formModel.get('emails')获取的是一个new FormArray对象,通过.controls取到内部元素集合 <li *ngFor="let email of this.formModel.get('emails').controls;let i = index;"> //由于FormArray使用的是索引值检索,故使用索引值作为模板的名字 //formControlName在此处为输入属性 邮箱:<input type="email" [formControlName]="i"/> </li> </ul> <div (click)="addEmail()">增加一个</div> </div> <button type="submit">保存</button> </form>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| //在ts中建立底层数据模型 formModel:FormGroup = new FormGroup({ //formControl在FormGroup中的写法 username:new FormControl(), dateRange:new FormGroup({ from: new FormControl(), to: new FormControl(), }), emails:new FormArray([ new FormControl(), ]) }); addEmail():void{ //通过this.formModel.get('emails')获取的是特定的FormArray的实例对象(类型为对象),需要转换成FormArray类型 var emails = this.formModel.get('emails') as FormArray; emails.push(new FormControl()); } onSubmit():void{ //获取表单的值 console.log(this.formModel.value); }
|
特别介绍
- formGroup和formGroupName用来绑定FormGroup
- formControl和formControlName用来绑定FormControl
- formArrayName用来绑定FormArray
Angular表单进阶
1 2 3 4 5 6 7 8 9 10 11
| formModel: FormGroup; constructor(public fb:FormBuilder){ this.formModel = fb.group({ username:[''], mobile:[''], passwords:fb.group({ psw_1:[''], psw_2:[''], }) }) }
|
表单校验
- 标准格式
1 2 3
| nameValidator(control:AbstractControl):{[key:string]:any}{ return null; }
|
从标准格式可以看出,校验之后返回的对象为null或者一个key为字符串的对象
- Angular提供的校验器:Validators.required、Validators.minLength(len)等等
1 2 3 4
| //使用自带校验器对username字段进行校验 //在数据模型中(formModel) //校验器以数组的形式出现 username: ['', [ Validator.required,Validator.minLength(6)] ]
|
1 2 3 4
| onSubmit():viod{ let isValid:boolean = this.formModel.get('username').valid; let error:any = this.formModel.get('username').error; }
|
对于单独的FormControl检验
1 2 3 4 5 6 7
| mobileValidator(control:FormControl):any{ let reg = /^[0-9]*$/; let valid = reg.test(control.value); //当校验通过时,返回null,否则返回一个空对象 return valid ? null : { mobile:true } }
|
- 注意校验器放置的位置
1
| mobile:['', this.mobileValidator]
|
对于放在FormGroup内部的FormControl的校验
1 2 3 4 5 6 7 8
| equalPswValidator(group:FormGroup):any{ let psw_1:FormControl = group.get('psw_1') as FormControl; let psw_2:FormControl = group.get('psw_2') as FormControl; let valid = ( psw_1.value === psw_2.value ); return valid ? null : { equalPsw:{msg:'我是错误提示信息!'} } }
|
- 注意校验器放置的位置和写法:{validator:this.equalPswValidator}
1 2 3 4
| passwords:fb.group({ psw_1:[''], psw_2:[''], },{validator:this.equalPswValidator})
|
异步校验器:
通过代码模拟http请求处理
1 2
| //只需要修改return语句即可,返回的是一个可观察对象 return Observable.of(valid? null:{mobile:{msg:'错误信息!'}}}).delay(3000);
|
1
| mobile:['',mobileValidator,asyncMobileValidator]
|
通过校验器可以做到的额外的事
- 通过校验器方法来控制组件中的提示语句隐藏或者显示:hasError、getError
- 获取独立的FormControl的校验结果:formModel.hasError(‘mobile’,’mobile’);
- 获取存在FormGroup内部的FormControl的校验结果:formModel.hasError(‘minLength’,[‘passwords’,’psw_1’])
- 获取独立的FormControl校验反馈的信息:formModel.getError(‘mobile’,’mobile’);
- ④获取存在FormGroup内部的FormControl校验反馈的信息:formModel.getError(‘equalPsw’,’passwords’) ? msg:null;
注意方法的第一个参数不是校验器的名称,而是校验器返回来的对象的key,即:
1 2
| return valid ? null : { equalPsw:true } 中的 equalPsw return valid ? null : { mobile:true } 中的mobile
|
在④中,如果发生错误,那么将会获取到返回的“错误提示信息”。
完整的代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <form [formGroup]="formModel" (submit)="onSubmit()"> <div> 用户名:<input type="text" formControlName="username"/> <div [hidden]="!formModel.hasError('minlength','username')"> 用户名输入不正确 </div> </div> <div> 手机号:<input type="text" formControlName="mobile"/> <div [hidden]="!formModel.hasError('mobile','mobile')"> <div>{{ formModel.getError('mobile','mobile')?.msg }}</div> </div> </div> <div formGroupName="passwords"> <div> 密码:<input type="text" formControlName="psw_1"/> <div [hidden]="!formModel.hasError('minlength',['passwords','psw_1'])"> 密码最小长度不符合要求 </div> </div> <div> 确认密码:<input type="text" formControlName="psw_2"/> <div [hidden]="!formModel.hasError('equalPsw','passwords')"> {{ formModel.getError('equalPsw','passwords')?.msg }} </div> </div> </div> <button type="submit">提交</button> </form>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| formModel:FormGroup; constructor(fb:FormBuilder){ this.formModel = fb.group({ username:['',[Validators.required,Validators.minLength(6)]], mobile:['',this.mobileValidator], //发现一个现象,如果此处加上Validators.required,程序会报错 passwords:fb.group({ psw_1:['',Validators.minLength(8)], psw_2:[''], },{validator:this.equalPswValidator}) }) } mobileValidator(control:FormControl):any{ let reg = /^[0-9]*$/; let valid = reg.test(control.value); return valid ? null : { mobile:{msg:'手机号码不正确!'} } } equalPswValidator(control:FormGroup):any{ let psw_1 = control.get('psw_1') as FormControl; let psw_2 = control.get('psw_2') as FormControl; let valid = (psw_1.value === psw_2.value); return valid ? null : { equalPsw:{msg:'密码错误提示信息!'}} } onSubmit(){ let valid = this.formModel.valid; console.log(valid); };
|
状态字段
touched/untouched:字段是否获取过焦点,如果获取过则:true/false,否则相反;
1 2 3
| <div [hidden]="!formModel.get('mobile').valid || formModel.get('mobile').untouched"> 当获得过焦点,且表单校验为false,那么显示这段错误提示信息! </div>
|
pristine/dirty:如果一个值未被修改过,则:true/false,否则相反;
1 2 3
| <div [hidden]="formModel.get('mobile').valid || formModel.get('mobile').pristine"> 当值被修改过,且不合法的情况下显示该提示信息! </div>
|
pending:当一个字段正处于异步校验中时,该字段为true