angular4.0的模板式表单、响应式表单及其错误提示
来源:互联网 发布:软件无线电原理与技术 编辑:程序博客网 时间:2024/05/22 12:11
两种表单写法,看了一遍之后最近做东西还是傻傻分不清,各种问题,所以滚去又看了一遍并做了记录~~~
共勉^_^
模板式表单
NgForm、NgModel、NgModelGroup是FormModule里的内容,NgForm会自动拦截标准的表单处理事件(eg.提交),angular用ngSubmit代替标准的表单提交NgForm隐式的创建一个FormGroup类的实例,代表表单的数据模型,用来存储表单数据,标有这个指令的表单会自动发现标有NgModel指令的表单子元素,并将它们的值添加到数据模型中
<div ngForm>...</div>等同于<form>...</form>
如果不想让angular管理你的表单,需要在表单标签里添加ngNoForm,此时浏览器默认行为生效(eg. action、post)
<form ngNoForm>...</form>
模板本地变量(#开头 eg.#myForm)
表单被模板本地变量引用,以便在模板中访问NgForm的实例
<form #myForm = "ngForm" (ngSubmit) = "onSubmit(myForm.value)">...已经拿到NgForm创建的对象了 <input type="text" ngModel> <input type="text" ngModel> <input type="text" ngModel></form><div>使用:{{myForm.value | json}}value保存着所有表单字段(仅限标有ngModel的字段)里的当前值</div>onSubmit(value: any) {console.log(value);}
ngModel会隐式的创建一个FormControl,代表字段的数据模型,在ngForm的表单里这个指令不需要用括号[()]括起来, 但需要为添加这个指令的字段添加一个name属性
<form #myForm = "ngForm" (ngSubmit) = "onSubmit(myForm.value)"> ...通过onSubmit函数传参,已经拿到NgForm创建的对象了 <input type="text" ngModel name="username"> <input type="password" ngModel name="password"> <input type="password" ngModel name="repassword"></form>
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
<!-- 注意括号的问题,[formGroup]的值是后台的一个属性,而formGroupName是字符串--><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()">增加Email</button> </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; //拿到一个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);}
formBulider
简化了表单处理的语法,本身没有什么特性
ts:
constructor(fb: FormBuilder) { this.formModel = fb.group({ username: ['', [Validators.required, Validators.minLength(6)]], mobile: ['', mobileValidator, mobileAsyncValidator], passwordsGroup: fb.group({ password: ['', Validators.minLength(6)], pconfirm: [''] }, {validator: equalValidator}) // 同时校验多个字段,即为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// 获取到这个元素的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); // console.log('密码校验结果是' + valid); 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'])">密码最小长度是6</div> <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/* touched、untouched关注的是有无聚焦,pristine、dirty关注值有没有被改变 ,只要有一个字段聚焦则整个表单都被聚焦,所有字段都未被聚焦,表单才未被聚焦,pristine、dirty同样*/
响应式表单状态错误提示
.eg.
/* 用户刚打开表单,所有字段都是空的显然不合法,但因为还未被用户访问所以显示错误信息不大美观,所以字段外面的div表示只有当该字段不合法且已被访问时才显示错误信息,*/<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: 1px 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下 ,不启用浏览器默认的校验<form novalidate> ...</form>
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>用户名:<input ngModel required minlength="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>用户名:<input ngModel required minlength="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); } }
模版式表单不太适合做复杂的场景,
- angular4.0的模板式表单、响应式表单及其错误提示
- Angular4模板式表单、响应式表单、表单状态字段
- angular4表单-响应式表单
- 一个angular4.0响应式表单应用~~~
- angular4表单—模板式表单
- angular4响应式表单与校验
- Angular4的表单
- Angular 的响应式表单
- Angular2响应式表单
- 普通表单与响应式表单的对比
- yii2 表单错误提示
- Ideal Forms响应式表单
- Angular2 响应式表单验证
- angular4中关于表单的校验
- Angular开发(十一)-关于响应式表单及表单的校验
- Html5添加超酷响应式表单美化模板插件教程
- Angular4学习笔记之表单
- angular4里一个响应式编程的小例子
- Android Xml文件生成,Xml数据格式写入
- 2017 JUST Programming Contest 3.0 H. Eyad and Math
- 51Nod-1837-砝码称重
- 用construct 2制作一个简单的飞机游戏
- Eclipse 单步调试
- angular4.0的模板式表单、响应式表单及其错误提示
- synchronized关键字
- 常见网络攻击及处理办法
- 奔小康赚大钱 HDU
- Java字符流、字节流的超类及子类的API记录和结构图备忘
- 2017 JUST Programming Contest 3.0 D. Dice Game
- 一行代码让你的TableView动起来-iOS动画
- DTD与XSD区别
- Android 自定义 View