写在前面
1表单类型
- 模板驱动型表单:使用模板表单内置指令、内置校验的方式来构建表单。也就是将代码、逻辑放在模版(HTML)里。
- 响应式表单:将功能性的内容移到代码里。
- 动态表单:用代码生成表单DOM。
2模板驱动表单
2.1表单指令
指令是对DOM的扩展,Angular内置了表单指令来扩展表单功能,如数据绑定、校验规则、显示校验错误信息等。
2.1.1NgForm指令
- NgForm指令是表单的控制中心,所有表单指令都需要在NgForm指令内部才能正常运行。
- NgForm需要在模块中引入。
- 在模版中,不需要在标签手动挂接NgForm指令,如果在模块中引入了FormsModule模块,Angular 自动创建NgForm指令,并把它附加到标签。
- NgForm指令控制了通过NgModule指令和name属性创建的控件类,并且会跟踪控件类的属性变化,包括有效性属性(valid)。
2.1.2NgModel指令
- 是ngModel,不是ngModule!我就犯了这个2b错误!
- NgModel指令是表单数据绑定的核心所在,几乎所有的表单特性都依赖NgModule指令实现。
- NgModel指令实现了表单控件的数据绑定,提供了控件状态跟踪及校验功能。
- 在控件中使用NgModel属性绑定,必须给该控件一个name属性,否则会报错。因为NgForm指令会为表单建立一个控件对象FormControl的集合,以此来作为表单控件的容器。控件的NgModel属性绑定会以name作为唯一标识符来注册并生成一个FormControl,将其加入到FormControl的集合中。
3常用表单控件
3.1单选框
单选框控件(Radio)实现双向数据绑定,同一组单选框控件的所有[(ngModel)]属性必须绑定同一个模型数据,且name属性名也必须相同:
<input type="radio" name="sex" [(ngModel)]="curContact.sex" value="female" />女
<input type="radio" name="sex" [(ngModel)]="curContact.sex" value="male" />男
3.2复选框
复选框控件(CheckBox)用来表示该表单复选框是否被选中,其中[(ngModel)]属性绑定的是一个布尔值:
<input type="checkbox" name="lock" [(ngModel)]="curContact.lock" />禁用
3.3单选下拉框
单选下拉框控件(Select)的双向数据绑定,需结合option元素绑定的值来实现。option选项的元素属性的绑定目标有两种,分别为value和ngVlue。当在option元素中使用value绑定数据时,其返回值类型是基本数据类型;当使用ngValue绑定数据时,其返回值类型是对象数据类型。
在构建下拉框前,需要先定义下拉框列表所需的数据:
export class FormComponent {
interests: any[] = [
{ value: 'reading', display: '阅读' },
{ value: 'traveling', display: '旅游'},
{ value: 'sport', display: '运动'}
];
}
<!-- 返回基本数据类型 -->
<select name="interestValue" [(ngModel)]="curContact.interestValue">
<option *ngFor="let interest of interests" [value]="interest.value">{{interest.display}}
</option>
</select>
<!-- 返回对象数据类型 -->
<select name="interestObj" [(ngModel)]="curContact.interestObj">
<option *ngFor="let interest of interests" [ngValue]="interest">{{interest.display}}</option>
</select>
3.4多选下拉框
多选下拉框控件(Multiple Select)实现了下拉选择多个选项的功能。多选下拉框的用法与单选下拉框类似,不同的是多选下拉框返回的数据是一个由所有被选项组数据组成的数组。
下面实现一个多选下拉框,返回一个成员为字符串的数组:
<select multiple name="interestMul" [(ngModel)]="curContact.interestMul">
<option *ngFor="let interest of interests" [value]="interest.value">
{{interest.display}}
</option>
</select>
当然,多选下拉框也可以绑定对象类型的数据,使用ngValue,与单选下拉框类似。
4模版局部变量(局部变量)
局部变量是模版中对DOM元素、指令、组件的*引用*,可以使用在当前元素、兄弟元素或任何子元素中。
4.1DOM元素局部变量
- DOM元素局部变量指向DOM元素。
- DOM元素局部变量不需要手动设置值,只需要在元素中添加属性即可。
在标签元素中定义DOM元素局部变量方式:
1. #局部变量名
2. ref-变量名
<li>
<label for="name">姓名:</label>
<input type="text" #contactName name="contactName" id="contactName">
<input type="text" ref-relNum name="telNum" id="telNum">
<p>{{contactName.value}} == {{telNum.value}}</p>
</li>
在模版的其他元素中可以直接使用该元素的DOM属性。
4.2表单指令局部变量
- 表单指令局部变量指向表单指令实例对象。
- 表单指令局部变量需要在定义时手动初始化特定指令的代表值。
4.2.1NgForm 表单局部变量
下面的例子,在表单中定义局部变量contactForm,将contactForm变量初始化为ngForm,并在表单控件加入ngModule及contactName属性:
<form #contactForm="ngForm">
<ul>
<li>
<label for="contactName">姓名:</label>
<input type="text" name="contactName" [(ngModel)]="curContact.name" />
</li>
<li>
<label for="telNum">电话:</label>
<input type="text" name="telNuM" [(ngModel)]="curContact.telNum" />
</li>
</ul>
</form>
局部变量contactForm为NgForm指令实例对象的引用,可以在模版中读取NgForm实例对象的属性值,如追踪表单的valid属性状态。当被包含的所有控件都有效时,contactForm.valid的值为true,否则为false。在控件中添加ngModel和name属性后,若往姓名控件中输入”李四”,电话控件中输入”123456789”,则contactForm.value的值为
{
name: '李四',
telNum: '123456789'
}
contactForm.value是一个简单的JSON对象,该对象是的键是对应控件元素的name属性值,而其值对应控件元素的value值。
NgModel控件局部变量
下面的例子是一个文本控件,将[{ngModel}]初始化为联系人姓名,并添加控件局部变量name:
<input type="text" name="contactName" [(ngModel)]="curContact.name" #contactName="ngModule" />
<p>{{contactName.valid}}</p>
局部变量contactName是对NgModel指令实例对象的引用,可以在模版中读取NgModel实例对象的属性值,如通过contactName.valid可以追踪控件状态、表单校验不通过时提示错误信息等。
Angular提供NgForm表单局部变量和NgModel控件局部变量,在模版中为追踪表单状态及表单的数据校验提供了便利。
表单状态
- NgForm和NgModel指令都可以用于追踪表单状态来实现数据校验。
- NgForm和NgModel都有五个表示状态的属性,值为布尔型,可以通过对应的局部变量获取。
- NgForm追踪的是整个表单控件的状态。
- NgModel追踪的是其所在表单控件的状态。
表单状态的属性语义如下:
状态 | true/false |
---|---|
valid | 表单值是否有效 |
pristine | 表单值是否未改变 |
dirty | 表单值是否已改变 |
touched | 表单是否已被访问过 |
untouched | 表单是否未被访问过 |
NgModelGroup指令
NgModelGroup指令可以对表单输入内容进行分组,方便我们在语义上区分不同类型的输入。例如联系人的信息包括姓名及住址,对这两者还可以进行更精细化的信息收集,如姓名可分为姓和名字,地址可分为城市、区、街等。通过NgModelGroup可以将姓名及住址进行分组收集:
<form #contactForm="ngForm">
<fieldset ngModelGroup="nameGroup" #nameGroup="ngModelGroup">
<label>姓:</label>
<input type="text" name="firstname" [(ngModel)]="curContact.firstname" required />
<label>名字:</label>
<input type="text" name="lastname" [(ngModel)]="curContact.lastname" required />
</fieldset>
<fieldset ngModelGroup="addressGroup" #addressGroup="ngModelGroup">
<label>街:</label>
<input type="text" name="street" [(ngModel)]="curContact.street" required />
<label>区:</label>
<input type="text" name="zip" [(ngModel)]="curContact.zip" required />
<label>城市:</label>
<input type="text" name="city" [(ngModel)]="curContact.city" required />
</fieldset>
</form>
上面例子中分别对联系人的姓名和住址进行了分组,通过ngModelGroup指令,将姓和名字的表单内容包裹成姓名分组,用nameGroup表示;将城市、区和街道的表单内容包过程住址分组,用addressGroup表示。此时contactForm.value的值为:
{
nameGroup:{
firstname: '',
lastname: ''
},
addressGroup:{
street: '',
zip: '',
city: ''
}
}
除此之外,NgModelGroup实例对象的valid属性可以单独校验其所在分组控件的输入是否有效。例如在上例的姓名分组中,只有当curContact.firstname、curContact.lasename输入都有效时,局部变量nameGroup.valid才会变为true,否则为false。
ngSubmit事件
ngSubmit事件可以相应表单里类型为submit的按钮操作,并负责表单的提交流程。
闲话少叙看代码:
<form #contactForm="ngForm" (ngSubmit)="doSubmit(contactForm.value)">
<!-- ... -->
<li class="form-group">
<button type="submit" class="btn btn-default" [disabled]="!contactForm.valid">添加</button>
<button type="reset" class="btn btn-default">重置</button>
</li>
</form>
提交按钮的处理逻辑:
export class FormComponent {
doSubmit(formValue: any){
//处理表单数据并提交
}
}
上面例子中,绑定了ngSubmit事件,ngSubmit事件的类型是EventEmitter。当提交按钮被点击后,首先执行表单原生的onSubmit事件,接着执行FormComponent组件中定义的doSubmit()方法。
自定义表单样式
NgModel指令不仅仅能够追踪表单控件的状态,还会根据表单控件的状态使用对应的CSS状态类来更新表单控件的类名。表单控件包括六个CSS状态类:
状态 | 为true时的css类 | 为false时的css类 |
---|---|---|
控件是否已经被访问过 | ng-touched | ng-untouched |
控件值是否已经变化 | ng-dirty | ng-pristine |
控件值是否有效 | ng-valid | ng-invalid |
表单控件的CSS类名会根据表单控件的状态变化而变化。可以方便写CSS来控制表单控件在不同状态使用不同的样式。
表单校验
表单校验是用来检查表单的输入值是否符合规则。HTML5表单内置了相关的基础校验,但能力有限。Angular封装了相关表单校验规则,并提供接口,以方便完成表单校验。
表单内置校验
Angular支持的表单内置校验包括:
- required:判断表单控件值是否为空。
- minlength:判断表单控件值的最小长度。
- maxlength:判断表单控件值的最大长度。
- pattern:判断表单控件值的匹配规则。
使用Angular表单内置校验与使用普通HTML校验一致,直接在表单控件中添加对应的校验属性即可。
<input type="text" minlength=3 maxlength=10 name="contactName" [(ngModel)]="curContact.name" required />
tips
HTML支持简单的拦截校验,但是其提示样式及文本是固定且不可控的,开发者可以在标签中添加novalidate属性来屏蔽HTML的拦截校验
表单自定义校验
当Angular表单内置校验无法满足需求的时候,就必须自己写校验规则了,也就是表单自定义校验。下面学习一下如何创建自定义校验。
创建自定义校验
下面自定义一个用户名的校验器,校验规则为:用户名必须是邮箱、手机号码的格式。
// validate-username.ts
import { FormComtrol } from '@angular/forms';
const EMAIL_REGEXP = new RegExp("[0-z0-9]+@[a-z0-9]+.com");
const TEL_REGEXP = new RegExp("1[0-9]{10}");
export function validateUserName(c: FormControl){
return (ENAIL_REGEXP.test(c.value) || TEL_REGEXP.test(c.value)) ? null : {
userName:{
valid: false,
errorMsg: '用户名必须是手机号或邮箱帐号'
}
};
}
使用自定义校验
- 这里只介绍在模型驱动方式构建的表单中如何使用自定义校验。
- 在模块中导入ReactiveFormsModule。
这里需要仔细学习一下再整理
import { Component } from '@angular.core';
import { FormGroup, FormControl } from '@angular.forms';
import { validateUserName } from './validate-username';
@Component({
selector: 'add-contact',
template: `
<form [formGroup]="customForm">
<label>姓名:</label>
<input type="text" formControlName="customName">
</form>
`
})
export class FormComponent {
customForm = new FormGroup({
customName: new FormControl('',validateUserName)
});
}
该例子定义了customForm和customName。在构建FormControl实例对象customName时传入的参数中,第一个参数为控件返回值的初始值,第二个参数为该控件的检验配置方法。
此外,检验配置可以使用Validators的内置校验,如Validators.required()、Validators.minLength()等,这与直接在表单控件元素上添加required、minlength属性效果一样。
使用Validators内置校验,需要先从@angular/forms导入Validators。
import { Validators } from '@angular/forms';
//...
customForm = new FormGroup({
customName: new FormControl('',Validators.minLength(4))
});
//...
如果需要在一个表单中添加多个校验器,可以在校验配置参数中使用数组,数组元素为对应的校验方法:
customForm = new FormGroup({
customName: new FormControl('',[Validators.minLength(4), ValidateUserName])
});
注意
如果你按照以上代码无法跑通,控制台报错
Can't bind to 'formGroup' since it isn't a known property of 'form'.
这是因为你没有在组件所属跟模块导入import { ReactiveFormsModule } from '@angular/forms'
。
我也脑抽了出现这个问题,研究了一下午为啥不对,最后发现,虽然模块里面import了,但是没有放在imports元数据中。白白浪费了一下午时间啊!!!
版权说明
本博客内所有揭秘angular2学习的文章都是学习《揭秘Angular2》—广发证券互联网金融技术团队著作这本书的学习总结及部分内容摘抄,若有侵权请与本人联系删除侵权内容。
qq:451354
邮箱:451354@qq.com