您的位置:首页 > Web前端 > AngularJS

Angular4表单(模板驱动表单、响应式表单、自定义表单验证)

2017-07-25 00:00 573 查看
摘要: Angular4表单

看了两天官网文档表单和表单验证后,才发现之前自己仅仅通过特定的 Angular CSS 类去控制去反馈用户的操作,如ng-touched等等是多么的入门,虽然说这已经够用了,但是之前写的都是在视图层上写逻辑,各种判断,导致后期维护有点困难。趁有时间记录一下这两天看了资料的一些心得。



模板驱动表单VS响应式表单

两者都可以通过表单绑定获取整个表单的值和是否合法eg: myForm.value, myForm.valid等等

Template-Driven Forms (模板驱动表单) 的特点

使用方便

适用于简单的场景

通过 [(ngModel)] 实现数据双向绑定

最小化组件类的代码

不易于单元测试

导入FormModule

表单绑定通过 #myForm="ngForm"

Reactive Forms (响应式表单) 的特点

比较灵活

适用于复杂的场景

简化了HTML模板的代码,把验证逻辑抽离到组件类中

方便的跟踪表单控件值的变化

易于单元测试

导入ReactiveFormsModule

表单绑定通过 [formGroup]="myForm"

组件类代码解释

Template-Driven Forms (模板驱动表单)

import { Component, AfterViewChecked, ViewChild } from '@angular/core';
import { NgForm } from '@angular/forms';
import { Hero }      from '../shared/hero';
@Component({
selector: 'hero-form-template2',
templateUrl: './hero-form-template2.component.html'
})
export class HeroFormTemplate2Component implements AfterViewChecked {

powers = ['Really Smart', 'Super Flexible', 'Weather Changer'];

hero = new Hero(18, 'Dr. WhatIsHisWayTooLongName', this.powers[0], 'Dr. What');

submitted = false;

onSubmit() {
this.submitted = true;
}

// Reset the form with a new hero AND restore 'pristine' class state
// by toggling 'active' flag which causes the form
// to be removed/re-added in a tick via NgIf
// TODO: Workaround until NgForm has a reset method (#6822)
active = true;

addHero() {
this.hero = new Hero(42, '', '');

this.active = false;
setTimeout(() => this.active = true, 0);
}
/*组件手动记录表单对象*/
heroForm: NgForm;

/*heroFrom变量是Angular从模板衍生出来的控制模型的引用。 我们利用@ViewChild来告诉Angular注入这个模型到组件类的currentForm*/
@ViewChild('heroForm') currentForm: NgForm;

/*监听视图中表格变化,第一次渲染表格对象有多少个值就跑多少次*/
ngAfterViewChecked() {
this.formChanged();
}

formChanged() {
/*检测是否发生变化*/
if (this.currentForm === this.heroForm) { return; }
this.heroForm = this.currentForm;
if (this.heroForm) {
/*heroForm得到的是FormModule对象,该对象继承于AbstractControl抽象类,里面包含touched等*/
/*valueChanges返回一个Observal,用于获取视图返回的可观察对象*/
this.heroForm.valueChanges
.subscribe(data => this.onValueChanged(data));
}
}

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] = '';
/*heroForm.form.get('name')获取对应表单里的键值*/
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 Forms (响应式表单) 的特点

import { Component, OnInit }                  from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';

import { Hero }                   from '../shared/hero';
import { forbiddenNameValidator } from '../shared/forbidden-name.directive';

@Component({
...
})
export class HeroFormReactiveComponent implements OnInit {

...
/**/
heroForm: FormGroup;
constructor(private fb: FormBuilder) { }

ngOnInit(): void {
/*初始化表单对象*/
this.buildForm();
}

buildForm(): void {
/*FormBuilder.group是一个用来创建FormGroup的工厂方法*/
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
}

/*与驱动表单上面代码类似*/
...
}


组件视图层对比

Template-Driven Forms (模板驱动表单)

表组通过ngModelGroup去绑定

<form #templateForm="ngForm" novalidate (ngSubmit)="onSubmit(templateForm)">
<span>Name</span>
<label class="user_name">
<input #uName="ngModel" type="text" name="user_name" [(ngModel)]="user.name" required>
</label>
<div ngModelGroup="account">
<span>QQ</span>
<label class="user_name">
<input type="text" name="user_qq" [(ngModel)]="user.account.qq" required>
</label>
<span>Password</span>
<label class="user_name">
<input type="password" name="user_password" [(ngModel)]="user.account.password" required>
</label>
</div>
<button style="width: 200px; height: 36px; border: 1px solid #ccc; margin-top: 20px;" [disabled]="templateForm.invalid">提交</button>
</form>


Reactive Forms (响应式表单) 的特点

表组通过

formGroupName="address"

-------------------------------------------------------------------

自定义表单验证

html:

这里注意要通过表单.get的方法获取FormModel的东西,reactiveForm.get('age').touched 或者reactive.controls.age?.touched

<form [formGroup]="reactiveForm" (ngSubmit)="onSubmit()">
<div class="form-group">
<label for="name">Name</label>
<input type="text" id="name" class="form-control" formControlName="name">
</div>
<div class="form-group" [ngClass]="{'has-error': (reactiveForm.get('age').touched || reactiveForm.get('age').dirty) && !reactiveForm.get('age').valid }">
<label class="col-md-2 control-label" for="ageId">年龄</label>

<div class="col-md-8">
<input class="form-control" id="ageId" type="number" placeholder="请输入年龄" formControlName="age" />
<span class="help-block" *ngIf="(reactiveForm.get('age').touched || reactiveForm.get('age').dirty) && reactiveForm.get('age').errors">
<span *ngIf="reactiveForm.get('age').errors.range">
输入年龄不合法
</span>
</span>
</div>
</div>
<p>{{reactiveForm.value | json}}</p>
</form>

ts:

...

@Component({
...
})
export class ReactiveFormComponent implements OnInit {

user: any = {
name: '',
age: 0
};

reactiveForm: FormGroup;

constructor(public fb: FormBuilder) { }

ngOnInit() {
this.buildForm();
}

buildForm(): void {
this.reactiveForm = this.fb.group({
'name': [this.user.name, Validators.required],
'age': [this.user.age, this.ageValidator]    //自定义验证方法使用
});

// this.reactiveForm.valueChanges
//   .subscribe(data => { console.log(data) });
}

/*有参数的验证器工厂函数写法,这里FormControl和下面的AbstractControl都是指向抽象类的属性和方法都是为了获取表单属性*/
private ageValidatorParams(min: number, max: number) {
return (c: FormControl): { [key: string]: any } | null => {
let age = c.value;
if (age && (isNaN(age) || age < min || age > max)) {
return { 'range': true, min: min, max: max };
}
return null;
}
}
/*必须返回一个对象,该对象为错误信息的值*/
private ageValidator(c: AbstractControl): { [key: string]: any } | null {
let age = c.value;
if (age && (isNaN(age) || age < 20 || age > 120)) {
return { 'range': true, min: 20, max: 120 };
}
return null;
}

}


参考文献:

1、Angular官方文档(表单验证)

2、模板驱动式表单

3、响应式表单

4、案例情况比较多的表单验证
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: