1. import { ReactiveFormsModule, FormGroup, FormControl, FormBuilder } from "@angular/forms";
  2. import { Validators } from "@angular/forms";

文档

共同基础

  • [FormControl](https://angular.cn/api/forms/FormControl) 实例用于追踪单个表单控件的值和验证状态。
  • [FormGroup](https://angular.cn/api/forms/FormGroup) 用于追踪一个表单控件组的值和状态。
  • [FormArray](https://angular.cn/api/forms/FormArray) 用于追踪表单控件数组的值和状态。
  • [ControlValueAccessor](https://angular.cn/api/forms/ControlValueAccessor) 用于在 Angular 的 [FormControl](https://angular.cn/api/forms/FormControl) 实例和原生 DOM 元素之间创建一个桥梁。

    表单中的数据流

    响应式(FormControl实例)

    视图中的每个表单元素都直接链接到一个表单模型(FormControl 实例)
  1. @Component({
  2. template: `
  3. Favorite Color: <input type="text" [formControl]="favoriteColorControl">
  4. `
  5. })
  6. export class FavoriteColorComponent {
  7. favoriteColorControl = new FormControl('');
  8. }

表单-文档 - 图1表单-文档 - 图2

View To Model

  1. 最终用户在输入框元素中键入了一个值,这里是 “Blue”。
  2. 这个输入框元素会发出一个带有最新值的 “input” 事件。
  3. 这个控件值访问器 [ControlValueAccessor](https://angular.cn/api/forms/ControlValueAccessor) 会监听表单输入框元素上的事件,并立即把新值传给 [FormControl](https://angular.cn/api/forms/FormControl) 实例。
  4. [FormControl](https://angular.cn/api/forms/FormControl) 实例会通过 valueChanges 这个可观察对象发出这个新值。
  5. valueChanges任何一个订阅者都会收到这个新值。

    Model To View

  6. favoriteColorControl.setValue() 方法被调用,它会更新这个 [FormControl](https://angular.cn/api/forms/FormControl) 的值。

  7. [FormControl](https://angular.cn/api/forms/FormControl) 实例会通过 valueChanges 这个可观察对象发出新值。
  8. valueChanges任何订阅者都会收到这个新值。
  9. 该表单输入框元素上的控件值访问器会把控件更新为这个新值。

    模板驱动式 (ngModel指令)

    在模板驱动表单中,每个表单元素都链接到一个指令上
  1. @Component({
  2. template: `
  3. Favorite Color: <input type="text" [(ngModel)]="favoriteColor">
  4. `
  5. })
  6. export class FavoriteColorComponent {
  7. favoriteColor = '';
  8. }

响应式表单

管理控件的值

显示表单控件的值

  • 通过可观察对象 valueChanges,你可以在模板中使用 [AsyncPipe](https://angular.cn/api/common/AsyncPipe) 或在组件类中使用 subscribe() 方法来监听表单值的变化。
  • 使用 value 属性。它能让你获得当前值的一份快照
  1. this.profileForm.valueChanges.subscribe(() => {});
  1. <p>Value: {{ name.value }}</p>

替换表单控件的值

  1. updateName() {
  2. this.name.setValue('Nancy');
  3. }

模板驱动表单

ngForm

你需要更多的工作来显示数据。在表单中声明一个模板变量。往

标签中加入 #heroForm=”ngForm”

  1. <form #heroForm="ngForm">

NgForm 指令

什么是 [NgForm](https://angular.cn/api/forms/NgForm) 指令? 但你明明没有添加过NgForm指令啊!
Angular 替你做了。Angular 会在 <form> 标签上自动创建并附加一个 [NgForm](https://angular.cn/api/forms/NgForm) 指令。
[_NgForm_](https://angular.cn/api/forms/NgForm) 指令为 _form_ 增补了一些额外特性。 它会控制那些带有 [_ngModel_](https://angular.cn/api/forms/NgModel) 指令和 _name_ 属性的元素,监听他们的属性(包括其有效性)。 它还有自己的 _valid_ 属性,这个属性只有在它包含的每个控件都有效时才是真。

name

注意, 标签还添加了 name 属性 (attribute),并设置为 “name”。当在表单中使用 [([ngModel](https://angular.cn/api/forms/NgModel))] 时,必须要定义 name 属性。

ngModel

通过 ngModel 跟踪修改状态与有效性验证
image.png

表单-文档 - 图4

reset

再次运行应用,点击 New Hero 按钮,表单被清空了。 输入框左侧的必填项竖条是红色的,表示 namepower 属性是无效的。 这可以理解,因为有一些必填字段。 错误信息是隐藏的,因为表单还是全新的,还没有修改任何东西。
输入名字,再次点击 New Hero 按钮。 这次,出现了错误信息!为什么?你不希望显示新(空)的英雄时,出现错误信息。
使用浏览器工具审查这个元素就会发现,这个 name 输入框并不是全新的。 表单记得你在点击 New Hero 前输入的名字。 更换了英雄对象并不会重置控件的“全新”状态。
你必须清除所有标记,在调用 newHero() 方法后调用表单的 _reset()_ 方法即可

  1. <button type="button" class="btn btn-default" (click)="newHero(); heroForm.reset()">New Hero</button>

ngSubmit

  1. <form (ngSubmit)="onSubmit()" #heroForm="ngForm">
  1. <button type="submit" class="btn btn-success" [disabled]="!heroForm.form.valid">Submit</button>

表单验证

简单验证

HTML5 有一组内置的属性,用来进行原生验证,包括 _required_[_minlength_](https://angular.cn/api/forms/MinLengthValidator)[_maxlength_](https://angular.cn/api/forms/MaxLengthValidator)

required

  1. profileForm = this.fb.group({
  2. firstName: ['', Validators.required]
  3. });

INVALID

  1. <p>
  2. Form Status: {{ profileForm.status }}
  3. </p>

模板驱动验证

你可以通过把 [ngModel](https://angular.cn/api/forms/NgModel) 导出成局部模板变量来查看该控件的状态。 比如下面这个例子就把 [NgModel](https://angular.cn/api/forms/NgModel) 导出成了一个名叫 name 的变量:

  1. <input id="name" name="name" class="form-control"
  2. required minlength="4" appForbiddenName="bob"
  3. [(ngModel)]="hero.name" #name="ngModel" >
  4. <div *ngIf="name.invalid && (name.dirty || name.touched)"
  5. class="alert alert-danger">
  6. <div *ngIf="name.errors.required">
  7. Name is required.
  8. </div>
  9. <div *ngIf="name.errors.minlength">
  10. Name must be at least 4 characters long.
  11. </div>
  12. <div *ngIf="name.errors.forbiddenName">
  13. Name cannot be Bob.
  14. </div>
  15. </div>

自定义验证器

  1. /** A hero's name can't match the given regular expression */
  2. export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
  3. return (control: AbstractControl): {[key: string]: any} | null => {
  4. const forbidden = nameRe.test(control.value);
  5. return forbidden ? {'forbiddenName': {value: control.value}} : null;
  6. };
  7. }

添加到响应式表单
添加到模板驱动表单

动态表单

  • 关键1:表单空间的div容器依靠 [formGroup] 指令来将模板 HTML 和底层的控件对象联系起来
  • 关键2: 总表单的form作为输入属性传入每个控件(引用传递),可在每个控件访问form