1:在AppModule模块里面引入 ReactiveFormsModule
要使用响应式表单,就要从@angular/forms包中导入ReactiveFormsModule,并把它添加到你的NgModule的imports数组中。
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
imports: [
// other imports ...
ReactiveFormsModule
],
})
export class AppModule { }
2:创建一个新的组件
ng g c NameEditor
3:请在组件中导入 FormControl 类
FormControl类是angular响应式表单最基本的构造快,要注册单个的表单控件,请在组件中导入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('');
}
4:在组件的模板中注册一个表单控件
修改模板,为表单控件添加 formControl 绑定,formControl 是由 ReactiveFormsModule 中的 FormControlDirective 提供的。
<label>
Name:
<input type="text" [formControl]="name">
</label>
<p>
Value: {{ name.value }}
</p>
使用这种模板绑定语法,把该表单控件注册给了模板中名为 name 的输入元素。这样,表单控件和 DOM
元素就可以互相通讯了:视图会反映模型的变化,模型也会反映视图中的变化。
5:替换表单控件的值
FormControl 提供了一个setValue()方法,他会修改这个表单控件的值。
js
updateName() {
this.name.setValue('Nancy');
}
html
<label>
Name:
<input type="text" [formControl]="name">
</label>
<p>
Value:{{name.value}}
</p>
<p>
<button (click)="updateName()">Update Name</button>
</p>
在这个例子中,你只使用单个控件FormControl,但是当调用 FormGroup 或 FormArray 的 setValue()
方法时,传入的值就必须匹配控件组或控件数组的结构才行6:把表单控件分组
FormControl的实例能控制单个输入框所对应的控件,FormGroup可以控制一组FormControl实例的表单状态,当创建FormGroup时,其中的每一个控件都会根据名字进行跟踪
1>:创建新的组件
ng g c ProfileEditor
2>:导入 FormGroup 和 FormControl 类并且创建 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({
firstName: new FormControl(''),
lastName: new FormControl(''),
});
}
现在这些单独的控件FormControl被收集到了一个控件组中FormGroup, FormGroup 实例拥有和 FormControl 实例相同的属性(比如 value、untouched)和方法(比如 setValue())。3>:关联FormGroup的模型和视图
FormGroup能追踪每个单独控件FormControl的状态和变化,如果其中某个控件的状态或值变化了,父控件也会一次新的状态变更或值变更事件
<form [formGroup]="profileForm">
<label>
First Name:
<input type="text" formControlName="firstName">
</label>
<label>
Last Name:
<input type="text" formControlName="lastName">
</label>
</form>
profileForm通过[formGroup]指令绑定到了 form
元素,在该模型和表单中的输入框之间创建了一个通讯层,FormControlName 指令提供的 formControlName 属性把每个输入框和 FormGroup 中定义的表单控件绑定起来。
4>:关联FormGroup的模型和视图
html
<form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
<label>
First Name:
</label>
<input type="text" formControlName="firstName">
<label>
Last Name:
</label>
<input type="text" formControlName="lastName">
<button type="submit" >Submit</button>
</form>
js
onSubmit () {
console.warn(this.profileForm.value);
}
form 标签所发出的 submit 事件是原生 DOM 事件,通过点击类型为 submit 的按钮可以触发本事件
6:嵌套的表单组
js
profileForm = new FormGroup({
firstName: new FormControl(''),
lastName: new FormControl(''),
address: new FormGroup({
street: new FormControl(''),
city: new FormControl(''),
state: new FormControl(''),
zip: new FormControl('')
})
});
html
<form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
<label>
First Name:
</label>
<input type="text" formControlName="firstName">
<label>
Last Name:
</label>
<input type="text" formControlName="lastName">
<div formGroupName="address">
<label>Streel</label>
<input type="text" formControlName="street">
<label>City</label>
<input type="text" formControlName="city">
<label>State</label>
<input type="text" formControlName="state">
<label>Zip Code</label>
<input type="text" formControlName="zip">
</div>
<button type="submit" [disabled]="!profileForm.valid">Submit</button>
</form>
部分模型修改
html
<button (click)="updateProfile()">Update Profile</button>
js
updateProfile() {
this.profileForm.patchValue({
firstName: 'Nancy',
address: {
street: '123 Drew Street'
}
});
}
patchValue() 方法要针对模型的结构进行更新。patchValue() 只会更新表单模型中所定义的那些属性。
6:使用 FormBuilder 来生成表单控件
FormBuilder 服务提供了一些便捷方法来生成表单控件。
FormBuilder在幕后也使用同样的方式来创建和返回这些实例,只是用起来更简单。 下面会重构 ProfileEditor 组件,用FormBuilder 来代替手工创建这些 FormControl 和 FormGroup。
Step 1 - 导入 FormBuilder 类
import { FormBuilder } from '@angular/forms';
Step 2 - 注入FormBuild 服务
constructor(private fb: FormBuilder) { }
Step 3- 生成表单控件
FormBuilder 服务有三个方法:control()、group() 和 array()。这些方法都是工厂方法,用于在组件类中分别生成
FormControl、FormGroup 和 FormArray。你可以使用 group() 方法,用和前面一样的名字来定义这些属性。这里,每个控件名对应的值都是一个数组,这个数组中的第一项是其初始值。你可以只使用初始值来定义控件,但是如果你的控件还需要同步或异步验证器,那就在这个数组中的第二项和第三项提供同步和异步验证器。
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: ['']
}),
});
constructor(private fb: FormBuilder) { }
}
7:简单的表单验证
如何把单个验证器添加到表单控件中,以及如何显示表单的整体状态。
Step 1 - 导入验证器函数
import { Validators } from '@angular/forms';
响应式表单包含了一组开箱即用的常用验证器函数。这些函数接收一个控件,用以验证并根据验证结果返回一个错误对象或空值。
Step 2 - 把字段设为必填
最常见的校验项是把一个字段设为必填项。本节描述如何为 firstName 控件添加“必填项”验证器。
在组件中,把静态方法 Validators.required 设置为 firstName 控件值数组中的第二项。
profileForm = this.fb.group({
firstName: ['', Validators.required],
lastName: [''],
address: this.fb.group({
street: [''],
city: [''],
state: [''],
zip: ['']
}),
});
HTML5 有一组内置的属性,用来进行原生验证,包括 required、minlength、maxlength等。虽然是可选的,不过你也可以在表单的输入元素上把它们添加为附加属性来使用它们。这里我们把 required 属性添加到 firstName
输入元素上。
<input type="text" formControlName="firstName" required>
这些 HTML5 验证器属性可以和 Angular
响应式表单提供的内置验证器组合使用。组合使用这两种验证器实践,可以防止在模板检查完之后表达式再次被修改导致的错误。
8:显示表单的状态
现在,你已经往表单控件上添加了一个必填字段,它的初始值是无效的(invalid)。这种无效状态冒泡到其父 FormGroup 中,也让这个 FormGroup 的状态变为无效的。你可以通过该 FormGroup 实例的 status 属性来访问其当前状态。
<p>
Form Status: {{ profileForm.status }}
</p>
9:使用表单数组管理动态控件
FormArray 是 FormGroup 之外的另一个选择,用于管理任意数量的匿名控件,如果你事先不知道子控件的数量,FormArray是一个很好的选择
Step 1 - 导入 FormArray
import { FormArray } from '@angular/forms';
Step 2 - 定义 FormArray
为 profileForm 添加一个 aliases 属性,把它定义为 FormArray 类型。(FormBuilder 服务用于创建 FormArray 实例。)
profileForm = this.fb.group({
firstName: ['张', Validators.required],
lastName: ['以'],
address: this.fb.group({
street: [''],
city: [''],
state: [''],
zip: ['']
}),
aliases: this.fb.array([
this.fb.control('')
])
});
Step 3 - 访问FormArray控件
通过 getter 来访问控件比较便捷,也容易复用
使用 getter 语法来创建一个名为 aliases 的类属性
get aliases() {
}
从父控件 FormGroup 中接收绰号的 FormArray 控件。
get aliases() {
return this.profileForm.get('aliases') as FormArray;
}
addAlias() {
this.aliases.push(this.fb.control(''));
}
Step 3 - 在模板中显示表单数组
在模型中定义了 aliases 的 FormArray 之后,你必须把它加入到模板*用户输入,使用 formArrayName 在这个
FormArray 和模板之间建立绑定。
<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 就会基于这个索引号提供它的控件。这将允许你在每次计算根控件的状态和值时跟踪每个控件。
全部代码
html
<form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
<label>
First Name:
</label>
<input type="text" formControlName="firstName" required>
<label>
Last Name:
</label>
<input type="text" formControlName="lastName">
<div formGroupName="address">
<h3>Address</h3>
<label>Streel</label>
<input type="text" formControlName="street">
<label>City</label>
<input type="text" formControlName="city">
<label>State</label>
<input type="text" formControlName="state">
<label>Zip Code</label>
<input type="text" formControlName="zip">
</div>
<div formArrayName="aliases">
<h3>Aliases</h3>
<button (click)="addAlias()">Add Alias</button>
<div *ngFor="let address of aliases.controls; let i=index">
<label>Alias</label>
<input type="text" [formControlName]="i" >
</div>
</div>
<button type="submit" [disabled]="!profileForm.valid">Submit</button>
<p>
<button (click)="updateProfile()">Update Profile</button>
</p>
<p>
Form Status: {{ profileForm.status }}
</p>
</form>
js
import { Component, OnInit } from '@angular/core';
import {FormControl, FormGroup, FormBuilder, Validators, FormArray} from '@angular/forms';
@Component({
selector: 'app-profile-editor',
templateUrl: './profile-editor.component.html',
styleUrls: ['./profile-editor.component.css']
})
export class ProfileEditorComponent implements OnInit {
profileForm = this.fb.group({
firstName: ['张', Validators.required],
lastName: ['以'],
address: this.fb.group({
street: [''],
city: [''],
state: [''],
zip: ['']
}),
aliases: this.fb.array([
this.fb.control('')
])
});
constructor(private fb: FormBuilder) {
}
ngOnInit() {
}
onSubmit () {
console.warn(this.profileForm.value);
}
updateProfile() {
this.profileForm.patchValue({
firstName: 'Nancy',
address: {
street: '123 Drew Street'
}
});
}
get aliases () {
return this.profileForm.get('aliases') as FormArray;
}
addAlias() {
this.aliases.push(this.fb.control(''));
}
}