class="markdown_views prism-tomorrow-night">
两种表单写法,看了一遍之后最近做东西还是傻傻分不清,各种问题,所以滚去又看了一遍并做了记录~~~
共勉^_^
模板式表单
NgForm、NgModel、NgModelGroup是FormModule里的内容,NgForm会自动拦截标准的表单处理事件(eg.提交),angular用ngSubmit代替标准的表单提交NgForm隐式的创建一个FormGroup类的实例,代表表单的数据模型,用来存储表单数据,
标有这个指令的表单会自动发现标有NgModel指令的表单子元素,并将它们的值添加到数据模型中
...
等同于
如果不想让angular管理你的表单,需要在表单标签里添加ngNoForm,此时浏览器默认行为生效(eg. action、post)
模板本地变量(#开头 eg.#myForm)
表单被模板本地变量引用,以便在模板中访问NgForm的实例
使用:{{myForm.value | json}}
value 保存着所有表单字段(仅限标有ngModel的字段)里的当前值
onSubmit(value : any) {
console.log(value );
}
ngModel会隐式的创建一个FormControl,代表字段的数据模型,在ngForm的表单里这个指令不需要用括号[()]括起来, 但需要为添加这个指令的字段添加一个name属性
name属性会成为前面所说value对象的一个属性名
标有ngModel的字段也可以用模板本地变量引用,从而获取它的值
<input type ="text" ngModel name ="username" #username = "ngModel ">
<p >
{{username.value }}
p >
NgModelGroup,是表单的一部分,允许将一部分表单字段组织在一起(比如密码和确认密码的input),形成更清晰的层次关系,隐式创建一个FormGroup类的实例,。。看代码更直接。。。(它的value值会在NgForm表单的那个value表现为一个嵌套的对象,所有NgModelGroup的 子属性是嵌套对象的子属性)
响应式表单
FormControl保存着当前元素的值以及校验状态,是否被修FormGroup
username:FormControl = new FormControl("aaa" )
参数表示当前元素初始值
FormGroup是多个FormControl的集合,若其中一个FormControl无效则整个FormGroup无效
username:FormControl = new FormControl("aaa" );
formGroup: FormGroup = new FormGroup({
form: new FormControl(),
to : new FormControl()
})
FormArray与FormGroup类似,但是它有一个额外的长度属性,FormGroup代表表单的
固定子集 ,而FormArray代表一个可以增长的集合,比如让用户输入多个Email地址
FormArray中的FormControl是没有相关的key,只能通过序号(0 1 2 3…)访问
emails: FormArray = new FormArray([
new FormControl("a@a.com" ),
new FormControl("b@a.com" )
]);
相关指令,都来自reactiveFormsModule模块
第二列是属性绑定,FormArray不能通过属性绑定来写,第三列不需要用属性绑定语法
响应式表单指令都是form开头的,,,,响应式表单可以直接访问数据模型的类,只能在代码中操作,模板式表单是只能在模板中操作
html
<form [formGroup ] = "formModel " (submit ) = "onSubmit ">
<input type ="text" formControlName = "username ">
<div formGroupName = "dateRange ">
起始日期:<input type ="date" formControlName ="form" >
截止日期:<input type ="date" formControlName ="to" >
div >
<div >
<ul formArrayName = "emails ">
<li *ngFor = "let e of this.formModel.get ("emails ").controls ; let i = index ;">
<input type ="text" [formControlName ] = "i "> li >
ul >
<button (click ) = "addEmail ()"> 增加Emailbutton >
div >
<div >
<button type = "submit "> 保存button >
div >
form >
ts
formModel: FormGroup = new FormGroup({
username:new FormControl("aaa" ),
dateRange: new FormGroup({
form: new FormControl(),
to: new FormControl()
}),
emails: FormArray = new FormArray([
new FormControl("a@a.com" ),
new FormControl("b@a.com" )
])
})
addEmail(){
let emails = this .formModel.get ("emails" ) as FormArray;
emails.push(new FormControl())
}
onSubmit() {
console.log(this .formModel.value );
}
在formgroup里要用formControlName
响应式的注册表单
html
<form [formGroup ]="formModel" (submit )="onSubmit()" >
<div > 用户名<input type ="text" formControlName ="username" > div >
<div > 手机号<input type ="text" formControlName ="mobile" > div >
<div formGroupName ="passwordsGroup" >
<div > 密码<input type ="password" formControlName ="password" > div >
<div > 确认密码<input type ="password" formControlName ="pconfirm" > div >
div >
<button type ="submit" > 注册button >
form >
ts
this .formModel = new FormGroup({
username: new FormControl(),
mobile: new FormControl(),
passwordsGroup: new FormGroup({
password: new FormControl(),
pconfirm: new FormControl()
})
});
onSubmit() {
console.log(this .formModel.value );
}
简化了表单处理的语法,本身没有什么特性
ts:
constructor (fb: FormBuilder) , ) // 同时校验多个字段,即为FormGroup 定义校验器
});
}
formBulider可以接受一个参数,eg. 第一个参数是formControl的初始值,第二个是校验方法,第三个是异步校验方法,如上面的代码,后面说校验的时候会说到,这里就先挂着,明白它是干嘛的
表单校验
angular的校验器:是一个普通的方法,接受一个参数,类型必须是AbstractControl,返回一个任意结构的对象,但这个对象的key必须是string类型,值任意
xxx(control: AbstractControl): {[key: string ]:any} {
return null ;
}
预定义校验器,在Forms里Validators对象里,minlength ,maxlength,required, pattern,用法上面有提到
this .formModel.get ("mobile" ).errors
响应式表单校验
我们可以把这些校验器放到一个ts文件里,通过export暴露,达到通用的目的。
validator/validators.ts
import {FormControl, FormGroup} from "@angular/forms";
import {Observable} from "rxjs/Observable";
import {observable} from "rxjs/symbol/observable";
export function mobileValidator (control: FormControl) : any {
var myreg = /^1 [3 |4 |5 |7 |8 ][0 -9 ]{9 }$/;
let valid = myreg.test(control.value);
console.log("mobile的校验结果是" + valid);
return valid ? null : {mobile : true };
}
export function equalValidator (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);
return valid ? null : {equal: {descs: "两次密码不一致" } };
}
在组件中就可以引用这个方法,ide自动添加了import…如果没有就要自己添加
passwordsGroup : fb .group ({
password : ['' , Validators.minLength(6 ) ],
pconfirm: ['' ]
}, {validator : equalValidator })
表单的状态提示hasError属性
[hidden] = "!formModel.hasError(' required' , 'username' )"
[hidden] = " !formModel. hasError('mobile' , 'mobile' )"
/* 因为返回的对象key是mobile所以第一个参数是mobile */
如果要校验的字段是一个formGroup里的formControl,hasError属性的第二个参数就是一个数组,如下:
<div formGroupName ="passwordsGroup" >
<div > 密码<input type ="password" formControlName ="password" > div >
<div [hidden ] = "!formModel.hasError ('minlength ', ['passwordsGroup ', 'password '])"> 密码最小长度是6div >
<div > 确认密码<input type ="password" formControlName ="pconfirm" > div >
<div [hidden ] = "!formModel.hasError ('equal ', 'passwordsGroup ')">
{{formModel.getError ('equal ', 'passwordsGroup ')?.descs }}
这个插值表达式直接显示出校验器里无效时的error
div >
div >
异步校验器
: 调用远程服务校验表单值,它返回一个可观测的流
export function mobileAsyncValidator (control: FormControl) : any {
var myreg = /^(((13 [0 -9 ]{1 })|(15 [0 -9 ]{1 })|(18 [0 -9 ]{1 })) + d{8 })$/;
let valid = myreg.test(control.value);
console.log("mobile的校验结果是" + valid);
return observable.of(valid ? null : {mobile : true }).delay(5000 );
}
状态字段
touched、untouched 判断是否获取过焦点
pristine(从未被改变过,值为true ,反之... . )、dirty(被修改过,值为true ) 判断字段的值有没有被改变过
pending 正处于异步校验时这个属性为true
响应式表单状态错误提示
.eg.
<div [hidden] = "formModel.get('username').valid || formModel.get('username').untouched" >
<div [hidden] = "!formModel.hasError('required', 'username')" >用户名是必填项div >
<div [hidden] = "!formModel.hasError('minlength', 'username')" >用户名最小长度是6 div >
div >
<div [hidden] = "formModel.get('mobile').valid || formModel.get('mobile').pristine" >
<div [hidden] = "!formModel.hasError('mobile', 'mobile')" >请输入正确的手机号div >
div >
浏览器会自动给字段加上class如ng-invalid,ng-dirty这些,这些都是根据用户输入同时改变的,可以通过这个给表单添加样式如
.ng-invalid {
border : 1 px solid red ;
}
但这个样式是只要不合法字段都有的,而且若有一个不合法表单也不合法,所以整个表单也会有这个样式,这样子很不美丽,我们可以规定指定字段的哪个状态有哪种样式
<div > 用户名<input [class.hasError ]="formModel.get('username').invalid && formModel.get('username').touched" type ="text" formControlName ="username" > div >
模版式表单校验
指令:
ng g directive directives/mobileValidator
指令比起组件是没有模版的,可以作为html属性来用
mobile-validator.directive.ts
import { Directive } from '@angular/core' ;
import {NG_VALIDATORS} from '@angular/forms' ;
import {mobileValidator} from '../validator/validators' ;
@Directive({
selector: '[mobile]' ,
providers: [{provide: NG_VALIDATORS, useValue: mobileValidator, multi: true}]
})
export class MobileValidatorDirective {
constructor() { }
}
useValue 值是要使用的校验器函数
multi: true 表示同一个token下可以挂多个属性,因为后面密码校验的指令也挂在这个token下 ,不启用浏览器默认的校验
equal-validator.directive.ts
import { Directive } from '@angular/core' ;
import {equalValidator} from '../validator/validators' ;
import {NG_VALIDATORS} from '@angular/forms' ;
@Directive ({
selector : '[equal]' ,
providers : [{provide : NG_VALIDATORS, useValue : equalValidator, multi : true }]
})
export class EqualValidatorDirective {
constructor() { }
}
reactive-register.html
<form #myForm = "ngForm " (ngSubmit ) = "onSubmit (myForm.value , myForm.valid )" novalidate >
<div > 用户名:<input ngModel required minlength ="6" name ="username" type ="text" (input )="onMobileInput(myForm)" > div >
<div > 手机号:<input ngModel mobile name = "mobile " type ="number" > div >
<div > 密码:<input ngModel minlength ="6" name ="password" type ="password" > div >
<div > 确认密码:<input ngModel name ="pconfirm" type ="password" > div >
div >
div >
<button type ="submit" > 注册button >
form >
因为模版式表单无法在代码里操作数据,只有通过onSubmit函数传进去,如上面写的
(ngSubmit) = "onSubmit(myForm.value, myForm.valid)"
reactive-register.ts
onSubmit(value: any , valid: boolean) {
console.log (valid);
console.log (value);
}
模版式表单状态错误提示
<div >用户名: "6" name ="username" type="text" (input)="onMobileInput(myForm)" >div >
<div [hidden] = "!myForm.form.hasError('required', 'username')" >用户名是必填项div >
<div [hidden] = "!myForm.form.hasError('minlength', 'username')" >用户名最小长度是六位div >
和上面一样的问题,用户一进入这个错误提示都在,显得表单很不美丽,做个优化如下:
<div >用户名: "6" name ="username" type="text" (input)="onMobileInput(myForm)" >div >
<div [hidden] = "mobileValid || mobileUntouched" >
<div [hidden] = "!myForm.form.hasError('required', 'username')" >用户名是必填项div >
<div [hidden] = "!myForm.form.hasError('minlength', 'username')" >用户名最小长度是六位div >
div >
.ts
mobileValid: boolean = true ;
mobileUntouched: boolean = true ;
onMobileInput(form: NgForm) {
if ( form ) {
console.log("ggggggggggggggggggggggggggggg" );
this .mobileValid = form.form.get ('username' ).valid;
this .mobileUntouched = form.form.get ('username' ).untouched;
console.log(this .mobileValid + '----------------------' + this .mobileUntouched);
}
}
模版式表单不太适合做复杂的场景,