简介
Angular 提供了两种不同的方法来通过表单处理用户输入:响应式表单和模板驱动表单。 两者都从视图中捕获用户输入事件、验证用户输入、创建表单模型、修改数据模型,并提供跟踪这些更改的途径。
选择一种方法
- 响应式表单提供对底层表单对象模型直接、显式的访问。更加健壮:它们的可扩展性、可复用性和可测试性都更高。
- 模板驱动表单依赖模板中的指令来创建和操作底层的对象模型。适合只在模板中管理非常基本的表单需求和逻辑。
关键差异
可伸缩性
- 响应式表单比模板驱动表单更有可伸缩性。它们提供对底层表单 API 的直接访问,以及对表单数据模型的同步访问,从而可以更轻松地创建大型表单。
- 模板驱动表单专注于简单的场景,可复用性没那么高。它们抽象出了底层表单 API,并且只提供对表单数据模型的异步访问。
建立表单模型
两种方法共享同一套底层构建块,只在如何创建和管理常用表单控件实例方面有所不同。
建立响应式表单
直接在组件类中定义表单模型。
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
@Component({
selector: 'app-reactive-favorite-color',
template: `
Favorite Color: <input type="text" [formControl]="favoriteColorControl">
`
})
export class FavoriteColorComponent {
favoriteColorControl = new FormControl('');
}
favoriteColorControl 是FormControl的实例,通过[formControl]="favoriteColorControl"实现了FormControl 实例与表单元素的联系。响应式表单通过输入元素上的 [formControl] 指令,在任何给定的时间点提供表单元素的值和状态。
建立模板驱动表单
import { Component } from '@angular/core';
@Component({
selector: 'app-template-favorite-color',
template: `
Favorite Color: <input type="text" [(ngModel)]="favoriteColor">
`
})
export class FavoriteColorComponent {
favoriteColor = '';
}
- 没有对 FormControl 实例的直接编程访问。
- 指令 NgModel 为指定的表单元素创建并管理一个 FormControl 实例。
表单中的数据流
响应式表单中的数据流
视图中的每个表单元素都直接链接到一个表单模型(FormControl 实例)。 从视图到模型的修改以及从模型到视图的修改都是同步的,而且不依赖于 UI 的渲染方式。
从视图到模型:
- 用户输入,触发input事件
- ControlValueAccessor监听到表单输入事件,将新值传递给FormControl实例
- FormControl实例通过valueChanges 这个可观察对象发出这个新值,valueChanges 的任何一个订阅者都会收到这个新值
从模型到视图:
- favoriteColorControl.setValue() 方法被调用,它会更新这个 FormControl 的值。
- FormControl 实例会通过 valueChanges 这个可观察对象发出新值,valueChanges 的任何订阅者都会收到这个新值。
- 控件值访问器ControlValueAccessor会把控件更新为这个新值。
模板驱动表单中的数据流
在模板驱动表单中,每一个表单元素都是和一个负责管理内部表单模型的NgModel 指令关联起来的。
从视图到模型:
- 用户输入"Blue",触发input事件
- ControlValueAccessor监听到表单输入事件,触发 FormControl 实例上的 setValue() 方法
- FormControl实例通过valueChanges 这个可观察对象发出这个新值,valueChanges 的任何一个订阅者都会收到这个新值
- ControlValueAccessory 还会调用 NgModel.viewToModelUpdate() 方法,它会发出一个 ngModelChange 事件。该组件模板双向数据绑定的 favoriteColor 属性就会修改为 ngModelChange 事件所发出的值(“Blue”)。
从模型到视图:
- 组件中修改了 favoriteColor 的值。变更检测开始。
- 在变更检测期间,由于输入框所绑定的值发生了变化,Angular 调用 NgModel 指令上的 ngOnChanges 生命周期钩子。ngOnChanges() 方法会把一个异步任务排入队列,以设置内部 FormControl 实例的值。变更检测完成。
- 在下一个检测周期,FormControl 实例通过可观察对象 valueChanges 发出最新值。valueChanges 的任何订阅者都会收到这个新值。
- ControlValueAccessor 使用 favoriteColor 的最新值来修改表单的输入框元素。
数据模型的可变性
变更追踪的方法对应用的效率有着重要影响。
- 响应式表单通过以不可变的数据结构提供数据模型,来保持数据模型的纯粹性。每当在数据模型上触发更改时,FormControl 实例都会返回一个新的数据模型,而不会更新现有的数据模型。这使你能够通过该控件的可观察对象跟踪对数据模型的唯一更改。这让变更检测更有效率,因为它只需在唯一性更改(译注:也就是对象引用发生变化)时进行更新。由于数据更新遵循响应式模式,因此你可以把它和可观察对象的各种运算符集成起来以转换数据。
- 模板驱动的表单依赖于可变性和双向数据绑定,可以在模板中做出更改时更新组件中的数据模型。由于使用双向数据绑定时没有用来对数据模型进行跟踪的唯一性更改,因此变更检测在需要确定何时更新时效率较低。
前面那些使用 favorite-color 输入元素的例子就演示了这种差异。
- 对于响应式表单,当控件值更新时,FormControl 的实例总会返回一个新值。
- 对于模板驱动的表单,favorite-color 属性总会被修改为新值。
总结
- 响应式表单使用 FormControl 实例实现,FormControl 控件的可观察对象可跟踪数据模型的唯一更改,变更检测效率高,
- 模板驱动表单依赖 NgModel 双向绑定实现。双向绑定没有用来对数据模型进行跟踪的唯一性更改,变更检测在需要确定何时更新时效率较低。