曾自己借助阿里云和hexo搭了个站点,现已废弃,过往写的博客暂挪到此处。
title: Angular2 表单校验
subtitle: 入门篇
date: 2016-10-05 00:02:04
tags:
- 技术
- Angular2
还是推荐直接阅读官方文档
简介
分两种方式:
- 模板驱动 Template Driven
同angular1的方式
使用模板驱动需要导入FormsModule
- Reactive Form
angular2更推崇的方式
使用Reactive Form需要导入ReactiveFormsModule;
实现
简单模板驱动
<label for="name">Name</label>
<input type="text" id="name" class="form-control"
required minlength="4" maxlength="24"
name="name" [(ngModel)]="hero.name"
#name="ngModel" >
<div *ngIf="name.errors && (name.dirty || name.touched)"
class="alert alert-danger">
<div [hidden]="!name.errors.required">
Name is required
</div>
<div [hidden]="!name.errors.minlength">
Name must be at least 4 characters long.
</div>
<div [hidden]="!name.errors.maxlength">
Name cannot be more than 24 characters long.
</div>
</div>
<ol>
<li>为表单添加angular指令,angular将创建对应的内部control模型。可以说模板上内含control模型;</li>
<li>在表单元素上添加html校验属性;</li>
<li>angular暴露control的状态,于是你可以在模板中安排对应control状态的报错信息</li>
<li>需给表单元素设置name属性,1)angular才能跟踪这个表单元素 2)angular为该元素创建对应的内部从control名为该name属性的值 3)angular才能将该元素同2)中的control关联起来</li>
<li>ngModel双向绑定</li>
<li>给元素设置模板变量#name(该变量名是不是得等于如4.2中的control名?待验证)的值为ngModel,将该元素control与ngModel关联起来,所以我们可以在模板中检查control的状态,实现3的功能</li>
</ol>
如果没有或者不需要为该元素设置ngModel,那么在元素上写上一个空的ngModel即可,不需要设置属性值
复杂模板驱动(校验信息在组件中定义)
html
<form #heroForm="ngForm" *ngIf="active" (ngSubmit)="onSubmit()">
<label for="name">Name</label>
<input type="text" id="name" class="form-control"
required minlength="4" maxlength="24" forbiddenName="bob"
name="name" [(ngModel)]="hero.name" >
<div *ngIf="formErrors.name" class="alert alert-danger">
{{ formErrors.name }}
</div>
</form>
component.ts
heroForm: NgForm;
@ViewChild('heroForm') currentForm: NgForm;
ngAfterViewChecked() {
this.formChanged();
}
formChanged() {
if (this.currentForm === this.heroForm) { return; }
this.heroForm = this.currentForm;
if (this.heroForm) {
this.heroForm.valueChanges
.subscribe(data => this.onValueChanged(data));
}
}
<ol>
<li>在form元素上通过模板变量#heroForm是一个angular模板上的control模型的引用</li>
<li>通过@ViewChild将该模型注入到组件类中的currentForm属性</li>
<li>调用ngAfterViewChecked确认是否有heroForm对象以及做相应的操作</li>
<li>有heroForm对象则订阅它的valueChanged(observable属性)</li>
<li>onValueChanged处理校验错误信息,传入的data包含当前元素的值</li>
</ol>
onValueChanged(data?: any) {
if (!this.heroForm) { return; }
const form = this.heroForm.form;
for (const field in this.formErrors) {
// clear previous error message (if any)
this.formErrors[field] = '';
const control = form.get(field);
if (control && control.dirty && !control.valid) {
const messages = this.validationMessages[field];
for (const key in control.errors) {
this.formErrors[field] += messages[key] + ' ';
}
}
}
}
formErrors = {
'name': '',
'power': ''
};
validationMessages = {
'name': {
'required': 'Name is required.',
'minlength': 'Name must be at least 4 characters long.',
'maxlength': 'Name cannot be more than 24 characters long.',
'forbiddenName': 'Someone named "Bob" cannot be a hero.'
},
'power': {
'required': 'Power is required.'
}
};
Reactive表单
<ol>
<li>在constructor中注入FormBuilder</li>
<li>ngOnInit中使用FormBuilder定义表单control模型和在该control模型的valueChanges事件时处理报错信息</li>
<li>form标签中关联组件中定义的control模型,通过指令formGroup</li>
<li>关联form中的各元素和对应的control,通过指令formControlName</li>
<li>不需要双向绑定的ngModel</li>
</ol>
heroForm: FormGroup;
constructor(private fb: FormBuilder) { }
ngOnInit(): void {
this.buildForm();
}
buildForm(): void {
this.heroForm = this.fb.group({
'name': [this.hero.name, [
Validators.required,
Validators.minLength(4),
Validators.maxLength(24),
forbiddenNameValidator(/bob/i)
]
],
'alterEgo': [this.hero.alterEgo],
'power': [this.hero.power, Validators.required]
});
this.heroForm.valueChanges
.subscribe(data => this.onValueChanged(data));
this.onValueChanged(); // (re)set validation messages now
}
Reactive表单中使用的新对象和方法 简单使用方式:
- FormBuilder声明对象指定例子表单中的三个control规格
- 每个control规格的属性名为该元素control名,属性值为数组;
- 该属性值数组的第一个值是该表单元素的当前值,第二个值一个校验函数或者一个校验函数的数组;
- angular有与标准html表单校验属性对应的内置的校验,如Validators.required,Validators.minLength(4),Validators.maxLength(24)。
- heroForm.value的值即该表单的值(heroForm为该表单form元素对应的control group);
- heroForm.get('单个元素的control名')则得到该元素的control;
- control.value为该元素的值
对比
模板驱动的校验规则写在html模板里;Reactive表单的校验规则在组件中定义
简单模板驱动的报错信息可以在html模板中定义;复杂模板驱动和Reactive表单的错误信息在组件中定义
错误信息在组件中定义则需要手动调用angular2生命周期及一些事件,来实现校验与错误信息的关联(这一点很不智能,记得bootstrap validation都可以自动关联,不知道angular会不会优化,还是有别的考虑);在模板中定义则不需要
-
模板驱动不用创建control模型,只需进行关联angular在模板上创建的control模型和该元素,方式如下:
- 元素的name属性关联angular的control
- ngModel双向绑定
- 给元素设置模板变量#name(该变量名是不是得等于如4.2中的control名?待验证)的值为ngModel,将control与ngModel关联起来,这样可以在模板中可以检查control的状态
而Reactive表单需要在组件中创建control,并在模板上关联,关联方式如下:
- form标签中关联组件中定义的control模型,通过指令formGroup
- 关联form中的各元素和对应的control,通过指令formControlName
以上是实现方式的对比,这些实现方式带来的结果如下:
<ol>
<li>模板驱动的校验和错误信息不能动态设置;</li>
<li>简单模板驱动的校验规则如果比较多,对应的报错信息多起来,html文件会太臃肿;</li>
<li>模板驱动将业务逻辑留在html中而不是js中,不太符合html和js各自的本义;(呃……原谅我对官方术语不熟)</li>
<li>Reactive表单的表单检验规则和报错信息之间的关联更易读,易于维护;</li>
<li>Reactive表单和复杂的模板驱动表单需要手动将报错信息与元素的校验规则对应起来,比模板驱动费事;</li>
<li>Reactive表单没有双向绑定,从组件到模板这个方向的信息传递得由程序员根据自己需要在组件中处理。</li>
</ol>
如何选择?
依我看。。。。
<ol>
<li>非常简单(少量校验错误信息和校验规则)的表单校验就用模板驱动好了</li>
<li>其他都推荐Reactive表单校验方式</li>
<li>Reactive表单有一个没有双向绑定的问题,这个问题我觉得大部分情况不是问题,
1)在定义control的时候可以设置初始值
2)从模板到组件这个方向的数据传输没问题
以上两点基本可以符合大部分表单,因为大部分表单都只有初始化和提交。
3)从组件到模板这个方向的数据传输可以通过事件绑定等在组件中自定义</li>
<li>虽然不推崇两种表单混用,因为两者的功能有共同的部分,会混淆,但是在Reactive表单中使用ngModel试过还没有问题</li>
</ol>
参考链接:
https://angular.cn/docs/ts/latest/cookbook/form-validation.html
http://blog.angular-university.io/introduction-to-angular-2-forms-template-driven-vs-model-driven/
还有一个很重要的自定义校验,见下篇>>>>>>>