Angular 4.x 中有两种表单:
Template-Driven Forms - 模板驱动式表单 (类似于 AngularJS 1.x 中的表单 )
Reactive Forms (Model-Driven Forms) - 响应式表单
Template-Driven Forms (模板驱动表单) ,我们之前的文章已经介绍过了,了解详细信息,请查看 - Angular 4.x Template-Driven Forms 。
Contents
ngModule and reactive forms
FormControl and FormGroup
Implementing our FormGroup model
Binding our FormGroup model
Reactive submit
Reactive error validation
Simplifying with FormBuilder
Form base and interface
Form base
接下来我们要实现的功能如下:
绑定 name、email、confirm 输入框的值
为所有输入框添加表单验证功能
显示验证异常信息
表单验证失败时,不允许进行表单提交
表单提交功能
User interface
// signup.interface.ts
export interface User {
name: string;
account: {
email: string;
confirm: string;
}
}
ngModule and reactive forms
在我们继续深入介绍 reactive forms 表单前,我们必须在 @NgModule
中导入 @angular/forms
库中的 ReactiveFormsModule
:
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
imports: [
...,
ReactiveFormsModule
],
declarations: [...],
bootstrap: [...]
})
export class AppModule {}
友情提示:若使用 reactive forms,则导入 ReactiveFormsModule;若使用 template-driven 表单,则导入 FormsModule。
Reactive approach
我们将基于上面的定义的基础表单,创建 SignupFormComponent
:
signup-form.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'signup-form',
template: `
`
})
export class SignupFormComponent {
constructor() {}
}
这是一个基础的组件,在我们实现上述功能前,我们需要先介绍 FormControl
、FormGroup
、FormBuilder
的概念和使用。
FormControl and FormGroup
我们先来介绍一下 FormControl 和 FormGroup 的概念:
FormControl - 它是一个为单个表单控件提供支持的类,可用于跟踪控件的值和验证状态,此外还提供了一系列公共API。
使用示例:
ngOnInit() {
this.myControl = new FormControl('Semlinker');
}
FormGroup - 包含是一组 FormControl 实例,可用于跟踪 FormControl 组的值和验证状态,此外也提供了一系列公共API。
使用示例:
ngOnInit() {
this.myGroup = new FormGroup({
name: new FormControl('Semlinker'),
location: new FormControl('China, CN')
});
}
现在我们已经创建了 FormControl
和 FormGroup
实例,接下来我们来看一下如何使用:
注意事项:Template-Driven Forms 中介绍的
ngModel
和name=""
属性,已经被移除了。这是一件好事,让我们的模板更简洁。
上面示例中,我们必须使用 [formGroup]
绑定我们创建的 myGroup
对象,除此之外还要使用 formControlName
指令,绑定我们创建的 FormControl
控件。此时的表单结构如下:
FormGroup -> 'myGroup'
FormControl -> 'name'
FormControl -> 'location'
Implementing our FormGroup model
signup.interface.ts
export interface User {
name: string;
account: {
email: string;
confirm: string;
}
}
与之对应的表单结构如下:
FormGroup -> 'user'
FormControl -> 'name'
FormGroup -> 'account'
FormControl -> 'email'
FormControl -> 'confirm'
是的,我们可以创建嵌套的 FormGroup 集合!让我们更新一下组件 (不包含初始数据):
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
@Component({...})
export class SignupFormComponent implements OnInit {
user: FormGroup;
ngOnInit() {
this.user = new FormGroup({
name: new FormControl(''),
account: new FormGroup({
email: new FormControl(''),
confirm: new FormControl('')
})
});
}
}
如果我们想要设置初始数据,我们可以按照上述示例进行设置。通常情况下,我们通过服务端提供的 API 接口来获取表单的初始信息。
Binding our FormGroup model
现在我们已经实例化了 FormGroup 模型,是时候绑定到对应的 DOM 元素上了。具体示例如下:
现在 FormGroup
与 FormControl
对象与 DOM 结构的关联信息如下:
// JavaScript APIs
FormGroup -> 'user'
FormControl -> 'name'
FormGroup -> 'account'
FormControl -> 'email'
FormControl -> 'confirm'
// DOM bindings
formGroup -> 'user'
formControlName -> 'name'
formGroupName -> 'account'
formControlName -> 'email'
formControlName -> 'confirm'
当使用模板驱动的表单时,为了获取 f.value
表单的值,我们需要先执行 #f="ngForm"
的操作。而对于使用响应式的表单,我们可以通过以下方式,方便的获取表单的值:
{{ user.value | json }} // { name: '', account: { email: '', confirm: '' }}
Reactive submit
跟模板驱动的表单一样,我们可以通过 ngSubmit
输出属性,处理表单的提交逻辑:
需要注意的是,我们使用 user
对象作为 onSubmit()
方法的参数,这使得我们可以获取表单对象的相关信息,具体处理逻辑如下:
export class SignupFormComponent {
user: FormGroup;
onSubmit({ value, valid }: { value: User, valid: boolean }) {
console.log(value, valid);
}
}
上面代码中,我们使用 Object destructuring
(对象解构) 的方式,从user
对象中获取 value
和 valid
属性的值。其中 value
的值,就是 user.value
的值。在实际应用中,我们是不需要传递 user
参数的:
export class SignupFormComponent {
user: FormGroup;
onSubmit() {
console.log(this.user.value, this.user.valid);
}
}
表单的数据绑定方式和提交逻辑已经介绍完了,是该介绍表单实际应用中,一个重要的环节 — 表单验证。
Reactive error validation
接下来我们来为表单添加验证规则,首先我们需要从 @angular/forms
中导入 Validators
。具体使用示例如下:
ngOnInit() {
this.user = new FormGroup({
name: new FormControl('', [Validators.required, Validators.minLength(2)]),
account: new FormGroup({
email: new FormControl('', Validators.required),
confirm: new FormControl('', Validators.required)
})
});
}
通过以上示例,我们可以看出,如果表单控制包含多种验证规则,可以使用数组声明多种验证规则。若只包含一种验证规则,直接声明就好。通过这种方式,我们就不需要在模板的输入控件中添加 required
属性。接下来我们来添加表单验证失败时,不允许进行表单提交功能:
那么问题来了,我们要如何获取表单控件的验证信息?我们可以使用模板驱动表单中介绍的方式,具体如下:
友情提示:?.prop 称为安全导航操作符,用于告诉 Angular prop 的值可能不存在。
此外我们也可以使用 FormGroup
对象提供的 API,来获取表单控件验证的错误信息:
现在我们来看一下完整的代码:
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { User } from './signup.interface';
@Component({
selector: 'signup-form',
template: `
`
})
export class SignupFormComponent implements OnInit {
user: FormGroup;
constructor() {}
ngOnInit() {
this.user = new FormGroup({
name: new FormControl('', [Validators.required, Validators.minLength(2)]),
account: new FormGroup({
email: new FormControl('', Validators.required),
confirm: new FormControl('', Validators.required)
})
});
}
onSubmit({ value, valid }: { value: User, valid: boolean }) {
console.log(value, valid);
}
}
功能是实现了,但创建 FormGroup
对象的方式有点繁琐,Angular 团队也意识到这点,因此为我们提供 FormBuilder
,来简化上面的操作。
Simplifying with FormBuilder
首先我们需要从 @angular/forms
中导入 FormBuilder
:
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
export class SignupFormComponent implements OnInit {
user: FormGroup;
constructor(private fb: FormBuilder) {}
...
}
然后我们使用 FormBuilder
对象提供的 group()
方法,来创建 FormGroup
和 FormControl
对象:
调整前的代码 (未使用FormBuilder):
ngOnInit() {
this.user = new FormGroup({
name: new FormControl('', [Validators.required, Validators.minLength(2)]),
account: new FormGroup({
email: new FormControl('', Validators.required),
confirm: new FormControl('', Validators.required)
})
});
}
调整后的代码 (使用FormBuilder):
ngOnInit() {
this.user = this.fb.group({
name: ['', [Validators.required, Validators.minLength(2)]],
account: this.fb.group({
email: ['', Validators.required],
confirm: ['', Validators.required]
})
});
}
对比一下调整前和调整后的代码,是不是感觉一下子方便了许多。此时更新完后完整的代码如下:
@Component({...})
export class SignupFormComponent implements OnInit {
user: FormGroup;
constructor(private fb: FormBuilder) {}
ngOnInit() {
this.user = this.fb.group({
name: ['', [Validators.required, Validators.minLength(2)]],
account: this.fb.group({
email: ['', Validators.required],
confirm: ['', Validators.required]
})
});
}
onSubmit({ value, valid }: { value: User, valid: boolean }) {
console.log(value, valid);
}
}
我有话说
Template-Driven Forms vs Reactive Forms
Template-Driven Forms (模板驱动表单) 的特点
使用方便
适用于简单的场景
通过 [(ngModel)] 实现数据双向绑定
最小化组件类的代码
不易于单元测试
Reactive Forms (响应式表单) 的特点
比较灵活
适用于复杂的场景
简化了HTML模板的代码,把验证逻辑抽离到组件类中
方便的跟踪表单控件值的变化
易于单元测试
参考资源
Angular 2 form fundamentals: reactive forms