angular学习之路12-响应式表单

1,添加单个表单控件:

步骤 1 - 注册 ReactiveFormsModule

要使用响应式表单,就要从 @angular/forms 包中导入 ReactiveFormsModule 并把它添加到你的 NgModule 的 imports 数组中。

import { ReactiveFormsModule } from '@angular/forms';

@NgModule({
  imports: [
    // other imports ...
    ReactiveFormsModule
  ],
})
export class AppModule { }

步骤 2 - 生成并导入一个新的表单控件

为该控件生成一个组件

ng generate component NameEditor

当使用响应式表单时,FormControl 类是最基本的构造块。要注册单个的表单控件,请在组件中导入 FormControl 类,并创建一个 FormControl 的新实例,把它保存在类的某个属性中。

import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';

@Component({
  selector: 'app-name-editor',
  templateUrl: './name-editor.component.html',
  styleUrls: ['./name-editor.component.css']
})
export class NameEditorComponent {
  name = new FormControl('');//此处保存fromcontrol到组件的那么属性当中
}

步骤 3 - 在模板中注册该控件

在组件类中创建了控件之后,你还要把它和模板中的一个表单控件关联起来。修改模板,为表单控件添加 formControl 绑定,formControl 是由 ReactiveFormsModule 中的 FormControlDirective 提供的。

<label>
  Name:
  <input type="text" [formControl]="name">  //使用这种模板绑定语法,把该表单控件注册给了模板中名为 name 的输入元素。这样,表单控件和 DOM 元素就可以互相通讯了:视图会反映模型的变化,模型也会反映视图中的变化。
</label>

显示表单控件的值

你可以用两种方式显示它的值:

  • 通过可观察对象 valueChanges,你可以在模板中使用 AsyncPipe 或在组件类中使用 subscribe() 方法来监听表单值的变化。

  • 使用 value 属性。它能让你获得当前值的一份快照。

<p>
  Value: {{ name.value }}
</p>

一旦你修改了表单控件所关联的元素,这里显示的值也跟着变化了。

替换表单控件的值

响应式表单还有一些方法可以用编程的方式修改控件的值,它让你可以灵活的修改控件的值而不需要借助用户交互。FormControl 提供了一个 setValue() 方法,它会修改这个表单控件的值,并且验证与控件结构相对应的值的结构。比如,当从后端 API 或服务接收到了表单数据时,可以通过 setValue() 方法来把原来的值替换为新的值。

下列的例子往组件类中添加了一个方法,它使用 setValue() 方法来修改 Nancy 控件的值。

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

2,把表单控件分组

1生成一个 ProfileEditor 组件并从 @angular/forms 包中导入 FormGroup 和 FormControl 类。

2 创建 FormGroup 实例

在组件类中创建一个名叫 profileForm 的属性,并设置为 FormGroup 的一个新实例。要初始化这个 FormGroup,请为构造函数提供一个由控件组成的对象,对象中的每个名字都要和表单控件的名字一一对应。

对此个人档案表单,要添加两个 FormControl 实例,名字分别为 firstName 和 lastName

import { Component } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';

@Component({
  selector: 'app-profile-editor',
  templateUrl: './profile-editor.component.html',
  styleUrls: ['./profile-editor.component.css']
})
export class ProfileEditorComponent {
  profileForm = new FormGroup({  //此处的名称要和模板中的名字一样
    firstName: new FormControl(''), //此处的名称要和模板中的名字一样
    lastName: new FormControl(''), //此处的名称要和模板中的名字一样
  });
}

现在,这些独立的表单控件被收集到了一个控件组中。这个 FormGroup 用对象的形式提供了它的模型值,这个值来自组中每个控件的值。 FormGroup 实例拥有和 FormControl 实例相同的属性(比如 valueuntouched)和方法(比如 setValue())。

3关联 FormGroup 的模型和视图

这个表单组还能跟踪其中每个控件的状态及其变化,所以如果其中的某个控件的状态或值变化了,父控件也会发出一次新的状态变更或值变更事件。该控件组的模型来自它的所有成员。在定义了这个模型之后,你必须更新模板,来把该模型反映到视图中。

<form [formGroup]="profileForm">//此处profileForm和组件中的名称保持一致
  
  <label>
    First Name:
    <input type="text" formControlName="firstName">  //此处firstName和组件中的名称保持一致
  </label>

  <label>
    Last Name:
    <input type="text" formControlName="lastName">/此处lastName和组件中的名称保持一致
  </label>

</form>

注意,就像 FormGroup 所包含的那控件一样,profileForm 这个 FormGroup 也通过 FormGroup 指令绑定到了 form 元素,在该模型和表单中的输入框之间创建了一个通讯层。 由 FormControlName 指令提供的 formControlName 属性把每个输入框和 FormGroup 中定义的表单控件绑定起来。这些表单控件会和相应的元素通讯,它们还把更改传递给 FormGroup,这个 FormGroup 是模型值的权威数据源。

保存表单数据

ProfileEditor 组件从用户那里获得输入,但在真实的场景中,你可能想要先捕获表单的值,等将来在组件外部进行处理。FormGroup 指令会监听 form 元素发出的 submit 事件,并发出一个 ngSubmit 事件,让你可以绑定一个回调函数。

把 onSubmit() 回调方法添加为 form 标签上的 ngSubmit 事件监听器。

<form [formGroup]="profileForm" (ngSubmit)="onSubmit()">

3,嵌套的表单组

步骤 1 - 创建嵌套的分组

“地址”就是可以把信息进行分组的绝佳范例。FormGroup 可以同时接纳 FormControl 和 FormGroup 作为子控件。这使得那些比较复杂的表单模型可以更易于维护、更有逻辑性。要想在 profileForm 中创建一个嵌套的分组,请添加一个内嵌的名叫 address 的元素指向这个 FormGroup 实例。

import { Component } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';

@Component({
  selector: 'app-profile-editor',
  templateUrl: './profile-editor.component.html',
  styleUrls: ['./profile-editor.component.css']
})
export class ProfileEditorComponent {
  profileForm = new FormGroup({ //此处是FormGroup
    firstName: new FormControl(''),
    lastName: new FormControl(''),
    address: new FormGroup({//FormGroup下面包含了一个FormGroup
      street: new FormControl(''),
      city: new FormControl(''),
      state: new FormControl(''),
      zip: new FormControl('')
    })
  });
}

在这个例子中,address group 把现有的 firstNamelastName 控件和新的 streetcitystate 和 zip 控件组合在一起。虽然 address 这个 FormGroup 是 profileForm 这个整体 FormGroup 的一个子控件,但是仍然适用同样的值和状态的变更规则。来自内嵌控件组的状态和值的变更将会冒泡到它的父控件组,以维护整体模型的一致性。

步骤 2 - 在模板中分组内嵌的表单

在修改了组件类中的模型之后,还要修改模板,来把这个 FormGroup 实例对接到它的输入元素。

把包含 streetcitystate 和 zip 字段的 address 表单组添加到 ProfileEditor 模板中。

<form [formGroup]="profileForm" (ngSubmit)="onSubmit()">

<label>

First Name:

<input type="text" formControlName="firstName" required>

</label>

 

<label>

Last Name:

<input type="text" formControlName="lastName">

</label>

 

<div formGroupName="address">

<h3>Address</h3>

<label>

Street:

<input type="text" formControlName="street">

</label>

<label>

City:

<input type="text" formControlName="city">

</label>

<label>

State:

<input type="text" formControlName="state">

</label>

<p>

Form Value: {{ profileForm.value | json }}

</p>

<p>

Form Status: {{ profileForm.status }}

</p>

<p>

<button (click)="updateProfile()">Update Profile</button>

</p>

 

部分模型更新

当修改包含多个 FormGroup 实例的值时,你可能只希望更新模型中的一部分,而不是完全替换掉。这一节会讲解该如何更新 AbstractControl 模型中的一部分。

修补(Patching)模型值

有两种更新模型值的方式:

  • 使用 setValue() 方法来为单个控件设置新值。 setValue() 方法会严格遵循表单组的结构,并整体性替换控件的值。

  • 使用 patchValue() 方法可以用对象中所定义的任何属性为表单模型进行替换。

setValue() 方法的严格检查可以帮助你捕获复杂表单嵌套中的错误,而 patchValue() 在遇到那些错误时可能会默默的失败。

在 ProfileEditorComponent 中,使用 updateProfile 方法传入下列数据可以更新用户的名字与街道住址。

updateProfile() {
  this.profileForm.patchValue({
    firstName: 'Nancy',
    address: {
      street: '123 Drew Street'
    }
  });
}

当点击按钮时,profileForm 模型中只有 firstName 和 street 被修改了。注意,street 是在 address 属性的对象中被修改的。这种结构是必须的,因为 patchValue() 方法要针对模型的结构进行更新。patchValue() 只会更新表单模型中所定义的那些属性。

4,使用 FormBuilder 来生成表单控件

当需要与多个表单打交道时,手动创建多个表单控件实例会非常繁琐。FormBuilder 服务提供了一些便捷方法来生成表单控件。FormBuilder 在幕后也使用同样的方式来创建和返回这些实例,只是用起来更简单。

步骤 1 - 导入 FormBuilder 类

从 @angular/forms 包中导入 FormBuilder 类。

import { FormBuilder } from '@angular/forms';

步骤 2 - 注入 FormBuilder 服务

FormBuilder 是一个可注入的服务提供商,它是由 ReactiveFormModule 提供的。只要把它添加到组件的构造函数中就可以注入这个依赖。

constructor(private fb: FormBuilder) { }

步骤 3 - 生成表单控件

FormBuilder 服务有三个方法:control()group() 和 array()。这些方法都是工厂方法,用于在组件类中分别生成 FormControlFormGroup 和 FormArray

用 group 方法来创建 profileForm 控件。

import { Component } from '@angular/core';
import { FormBuilder } from '@angular/forms';

@Component({
  selector: 'app-profile-editor',
  templateUrl: './profile-editor.component.html',
  styleUrls: ['./profile-editor.component.css']
})
export class ProfileEditorComponent {
  profileForm = this.fb.group({//创建表单组
    firstName: [''],
    lastName: [''],
    address: this.fb.group({//创建表单组
      street: [''],
      city: [''],
      state: [''],
      zip: ['']
    }),
  });

在上面的例子中,你可以使用 group() 方法,用和前面一样的名字来定义这些属性。这里,每个控件名对应的值都是一个数组,这个数组中的第一项是其初始值。

  constructor(private fb: FormBuilder) { }
}

5,简单表单验证

步骤 1 - 导入验证器函数

响应式表单包含了一组开箱即用的常用验证器函数。这些函数接收一个控件,用以验证并根据验证结果返回一个错误对象或空值。

从 @angular/forms 包中导入 Validators 类。

import { Validators } from '@angular/forms';

步骤 2 - 把字段设为必填(required)

最常见的校验项是把一个字段设为必填项。本节描述如何为 firstName 控件添加“必填项”验证器。

在 ProfileEditor 组件中,把静态方法 Validators.required 设置为 firstName 控件值数组中的第二项。

profileForm = this.fb.group({
  firstName: ['', Validators.required],//此处设置firstName为必填项
  lastName: [''],
  address: this.fb.group({
    street: [''],
    city: [''],
    state: [''],
    zip: ['']
  }),
});

HTML5 有一组内置的属性,用来进行原生验证,包括 requiredminlengthmaxlength 等。虽然是可选的,不过你也可以在表单的输入元素上把它们添加为附加属性来使用它们。这里我们把 required 属性添加到 firstName 输入元素上。

<input type="text" formControlName="firstName" required>

显示表单状态

当你往表单控件上添加了一个必填字段时,它的初始值是无效的(invalid)。这种无效状态会传播到其父 FormGroup 元素中,也让这个 FormGroup 的状态变为无效的。你可以通过该 FormGroup 实例的 status 属性来访问其当前状态。

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

提交按钮被禁用了,因为 firstName 控件的必填项规则导致了 profileForm 也是无效的。在你填写了 firstName 输入框之后,该表单就变成了有效的,并且提交按钮也启用了。

6,使用表单数组管理动态控件

FormArray 是 FormGroup 之外的另一个选择,用于管理任意数量的匿名控件。像 FormGroup 实例一样,你也可以往 FormArray 中动态插入和移除控件,并且 FormArray 实例的值和验证状态也是根据它的子控件计算得来的。 不过,你不需要为每个控件定义一个名字作为 key,因此,如果你事先不知道子控件的数量,这就是一个很好的选择。下面的例子展示了如何在 ProfileEditor 中管理一组绰号(aliases)。

步骤 1 - 导入 FormArray

从 @angular/form 中导入 FormArray,以使用它的类型信息。FormBuilder 服务用于创建 FormArray 实例。

import { FormArray } from '@angular/forms';

步骤 2 - 定义 FormArray

你可以通过把一组(从零项到多项)控件定义在一个数组中来初始化一个 FormArray。为 profileForm 添加一个 aliases属性,把它定义为 FormArray 类型。

使用 FormBuilder.array() 方法来定义该数组,并用 FormBuilder.control() 方法来往该数组中添加一个初始控件。

profileForm = this.fb.group({
  firstName: ['', Validators.required],
  lastName: [''],
  address: this.fb.group({
    street: [''],
    city: [''],
    state: [''],
    zip: ['']
  }),
  aliases: this.fb.array([
    this.fb.control('')//为formArray添加一个初始组件
  ])
});

FormGroup 中的这个 aliases 控件现在管理着一个控件,将来还可以动态添加多个。

步骤 3 - 访问 FormArray 控件

相对于重复使用 profileForm.get() 方法获取每个实例的方式,getter 可以让你轻松访问表单数组各个实例中的别名。 表单数组实例用一个数组来代表未定数量的控件。通过 getter 来访问控件很方便,这种方法还能很容易地重复处理更多控件。

使用 getter 语法创建类属性 aliases,以从父表单组中接收表示绰号的表单数组控件。

get aliases() {
  return this.profileForm.get('aliases') as FormArray;
}

定义一个方法来把一个绰号控件动态插入到绰号 FormArray 中。用 FormArray.push() 方法把该控件添加为数组中的新条目。

addAlias() {
  this.aliases.push(this.fb.control(''));
}

步骤 4 - 在模板中显示表单数组

要想为表单模型添加 aliases ,你必须把它加入到模板中供用户输入。和 FormGroupNameDirective 提供的 formGroupName 一样,FormArrayNameDirective 也使用 formArrayName 在这个 FormArray 实例和模板之间建立绑定。

在 formGroupName <div> 元素的结束标签下方,添加一段模板 HTML。

<div formArrayName="aliases">
  <h3>Aliases</h3> <button (click)="addAlias()">Add Alias</button>

  <div *ngFor="let address of aliases.controls; let i=index">
    <!-- The repeated alias template -->
    <label>
      Alias:
      <input type="text" [formControlName]="i">
    </label>
  </div>
</div>

每当新的 alias 加进来时,FormArray 的实例就会基于这个索引号提供它的控件。这将允许你在每次计算根控件的状态和值时跟踪每个控件。

添加绰号

最初,表单只包含一个绰号字段,点击 Add Alias 按钮,就出现了另一个字段。你还可以验证由模板底部的“Form Value”显示出来的表单模型所报告的这个绰号数组。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值