1.
简介
建立表单模型
响应式表单和模板驱动表单都是用表单模型来跟踪 Angular 表单和表单输入元素之间值的变化。下面的例子展示了如何定义和创建表单模型。
在响应式表单中建立
下面是一个带有输入字段的组件,它使用响应式表单实现了单个控件。
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('');//此处就是表单模型,建立在组件类中的,是显式的
}
在模板驱动表单中建立
下面是同一个带有输入字段的组件,它使用模板驱动表单实现了单个控件。
import { Component } from '@angular/core';
@Component({
selector: 'app-template-favorite-color',
template: `
Favorite Color: <input type="text" [(ngModel)]="favoriteColor"> //双向数据绑定 绑定到组件类中的favoriteColor属性上
`
})
export class FavoriteColorComponent {
favoriteColor = '';
}
在模板驱动表单中,权威数据源是模板。
模板驱动表单的 NgModel
指令负责创建和管理指定表单元素上的表单控件实例。它不那么明显,但你不必再直接操纵表单模型了,所以是隐式的
响应式表单中的数据流
如前所述,在响应式表单中,视图中的每个表单元素都直接链接到一个表单模型(FormControl
实例)。 从视图到模型的修改以及从模型到视图的修改都是同步的,不依赖于所呈现的 UI
下面这些步骤列出了 "从视图到模型" 数据流的梗概。
-
最终用户在输入框元素中键入了一个值,这里是 "Blue"。
-
这个输入框元素会发出一个带有最新值的 "input" 事件。
-
这个控件值访问器
ControlValueAccessor
会监听表单输入框元素上的事件,并立即把新值传给FormControl
实例。 -
FormControl
实例会通过valueChanges
这个可观察对象发出这个新值。 -
valueChanges
的任何一个订阅者都会收到这个新值。
下面这些步骤列出了从模型到视图的数据流的梗概。
-
favoriteColorControl.setValue()
方法被调用,它会更新这个FormControl
的值。 -
FormControl
实例会通过valueChanges
这个可观察对象发出新值。 -
valueChanges
的任何订阅者都会收到这个新值。 -
该表单输入框元素上的控件值访问器会把控件更新为这个新值。
模板驱动表单中的数据流
在模板驱动表单中,每个表单元素都链接到一个指令上,该指令负责管理其内部表单模型。下图使用相同的 "喜欢的颜色" 示例来演示当输入字段的值的变更来自视图和来自模板时,数据如何流动。
下面这些步骤列出了当输入框的值从 Red 变成 Blue 时 "从视图到模型" 的数据流概况。
-
最终用户在输入框元素中敲 "Blue"。
-
该输入框元素会发出一个 "input" 事件,带着值 "Blue"。
-
附着在该输入框上的控件值访问器会触发
FormControl
实例上的setValue()
方法。 -
FormControl
实例通过valueChanges
这个可观察对象发出新值。 -
valueChanges
的任何订阅者都会收到新值。 -
控件值访问器
ControlValueAccessory
还会调用NgModel.viewToModelUpdate()
方法,它会发出一个ngModelChange
事件。 -
由于该组件模板双向数据绑定到了
favoriteColor
,组件中的favoriteColor
属性就会修改为ngModelChange
事件所发出的值("Blue")。
下面这些步骤列出了当 favoriteColor
从 Blue 变为 Red 时,"从模型到视图" 的数据流概况。
-
组件中修改了
favoriteColor
的值。 -
变更检测开始。
-
在变更检测期间,由于这些输入框之一的值发生了变化,Angular 就会调用
NgModel
指令上的ngOnChanges
生命周期钩子。 -
ngOnChanges()
方法会把一个异步任务排入队列,以设置内部FormControl
实例的值。 -
变更检测完成。
-
在下一个检测周期,用来为
FormControl
实例赋值的任务就会执行。 -
FormControl
实例通过可观察对象valueChanges
发出最新值。 -
valueChanges
的任何订阅者都会收到这个新值。 -
控件值访问器
ControlValueAccessor
会使用favoriteColor
的最新值来修改表单的输入框元素。
表单验证
验证是管理任何表单时必备的一部分。无论你是要检查必填项,还是查询外部 API 来检查用户名是否已存在,Angular 都会提供一组内置的验证器,以及创建自定义验证器所需的能力。
-
响应式表单把自定义验证器定义成函数,它以要验证的控件作为参数。
-
模板驱动表单和模板指令紧密相关,并且必须提供包装了验证函数的自定义验证器指令。
可变性
追踪变更的方法对于应用的运行效率有着重要作用。
-
响应式表单通过将数据模型提供为不可变数据结构来保持数据模型的纯粹性。每当在数据模型上触发更改时,
FormControl
实例都会返回一个新的数据模型,而不是直接修改原来的。这样能让你通过该控件的可观察对象来跟踪那些具有唯一性的变更。这种方式可以让变更检测更高效,因为它只需要在发生了唯一性变更的时候进行更新。它还遵循与操作符相结合使用的 "响应式" 模式来转换数据。 //意思是响应式表单每次修改之后会生成一个变化后的新值,此值具有唯一性,这使得变更更快速 -
模板驱动表单依赖于可变性,它使用双向数据绑定,以便在模板中发生变更时修改数据模型。因为在使用双向数据绑定时无法在数据模型中跟踪具有唯一性的变更,因此变更检测机制在要确定何时需要更新时效率较低。//意思是模板驱动表单修改的都是之前的值,在之前的值的基础上修改成一个新值了,不同于响应式表单,每次修改是生成一个修改后的另外的新值,而不是在原来基础上修改
以 "喜欢的颜色" 输入框元素为例来看看两者有什么不同:
-
对于响应式表单,每当控件值变化时,
FormControl
实例就会返回一个新的值。 -
对于模板驱动表单,favoriteColor 属性总是会修改成它的新值。
最后的思考
要选择一项策略就要先了解所提供选项的优缺点。当决定在 Angular 中构建表单要选择哪种基础设施时,底层 API 访问、表单模型访问、可预测性、可变性、直观的验证方式和测试策略以及可伸缩性都是重要的考虑因素。 模板驱动表单和 AngularJS 中的传统模式相似,但它们具有局限性。响应式表单已经和 Angular 架构的其它部分存在的响应式模式相整合,并很好地弥补了这些需求。