前言
Reactive forms提供了一种model-driven
的方法来处理表单中会随时间变化的输入(数据),本篇中介绍着如何控制与更新一个表单、使用多个form-group、表单验证等等的基本表单操作。
Overview of reactive forms
Reactive forms使用了显式且不变
的方法管理表单状况,对于表单的状态的更改每次都会回传一个新的表单状态
,由于每次都会回传一个新的状态,所以他可以保持每次该改之间的model完整性,Reactive forms是围绕着observable
所建立的,表单中的输入(数值)会提供给input flow作为他的值并可以以同步的方式访问。
Reactive forms与template forms不同,Reactive forms通过对数据模型的同步访问提供了较高的可预测性
、observable operators的不变性
和可以透过observable streams观察数据的变更
。
Adding a basic form control
建立Reactive forms的三个步骤:
在你的app中注册reactive foms module
,这个module中含有使用Reactive forms的指令。在Component中新增一个新的FormControl instance
。在templete中注册FormControl。Register the reactive forms module
使用import关键字将ReactiveFormsModule
添加到app.module中的import阵列中。
// src/app/app.module.ts import { ReactiveFormsModule } from '@angular/forms';@NgModule({ imports: [ // other imports ... ReactiveFormsModule ],})export class AppModule { }
接下来透过cli建立一个 name-editor
Component,并将FormControl
class加入到Component中并将他实体化。
// src/app/name-editor/name-editor.component.tsimport { Component } from '@angular/core';import { FormControl } from '@angular/forms'; // import FormControl Class@Component({ selector: 'app-name-editor', templateUrl: './name-editor.component.html', styleUrls: ['./name-editor.component.css']})export class NameEditorComponent { name = new FormControl(''); // instance FormControl class}
Register the control in the template
在name-editor
的templete中建立可以与表单控制元件连动的网页元件,使用formControl
绑定templete中的元件。
<!-- src/app/name-editor/name-editor.component.html --><label> Name: <input type="text" [formControl]="name"></label>
在画面中显示出来
<!-- src/app/app.component.html --><app-name-editor></app-name-editor>
Displaying a form control value
可以透过两种方法显示forms中的数值:
可以透过valueChange()
中的subscription()
方法监听表单中的值。使用FormControl中的value
属性。name = new FormControl('');this.name.valueChanges.subscribe(val => console.log(val));
Value : {{name.value}}
Grouping form controls
一个表单中会包含几个相关的控制元件,Reactive forms提供两种方法可以将多个相关的控制组件分组为单个输入形式
。
透过cli建立一个新组建,使用import将FormGroup
与FormControl
加入Component中并将他们实例化。
// src/app/profile-editor/profile-editor.component.ts (form group)import { Component } from '@angular/core';import { FormGroup, FormControl } from '@angular/forms'; //import two class@Component({ selector: 'app-profile-editor', templateUrl: './profile-editor.component.html', styleUrls: ['./profile-editor.component.css']})export class ProfileEditorComponent { // instance FormGroup & FormControl profileForm = new FormGroup({ firstName: new FormControl(''), lastName: new FormControl(''), });}
将firstName
与lastName
这两个相关的控制组件透过FormGroup
分组为一个群组。
Create a nested groups
可以透过嵌套FromGroups来达到将较为複杂的表单内容分割为更小的Group,以方便管理以维护。
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(''), address: new FormGroup({ street: new FormControl(''), city: new FormControl(''), state: new FormControl(''), zip: new FormControl('') }) });}
将原本的profileForm Group中新增了第二层的嵌套Group,将street、city、state、zip绑定为一个FormGroup,透过这样的绑定可以将原本的profileForm保持完整的前提下再新增出一个Group。
<form [formGroup]="profileForm"> <label> First Name: <input type="text" formControlName="firstName"> </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> <label> Zip Code: <input type="text" formControlName="zip"> </label> </div> <button type="submit" [disabled]="!profileForm.valid">Submit</button></form>
Updating parts of the data model
当需要更新多个控制组件的值时,Angular提供两种方法可以改变值,一种是可以更改整组表单的方法,另一种是更改表单部分内容的方法。
使用setValue()
可以更新整个表单内容,他必须要遵守整个表单的结构
。使用patchValue()
可以替换表单内的部分内容。使用setValue()会严格检查整个表单的结构,若结构错误则会发生嵌套错误,若是使用patchValue()在这个错误上会默默地失败。
updateProfile() { this.profileForm.patchValue({ firstName: 'Nancy', address: { street: '123 Drew Street' } });}
Using the FormBuilder service to generate controls
对于在处理多个表单的时候还是使用手动的方式创建表单控制组件实例,这样的话可能会遇到重複宣告的问题,所以可以使用FormBuilder
,它提供了用于生成控制组件的便利方法。
Import the FormBuilder class
import { FormBuilder } from '@angular/forms';
Inject the FormBuilder service
在Component的comstructor中Inject FormBuilder service
。
constructor(private fb: FormBuilder) { }
Generate form controls
FormBuilder提供control()
、group()
、array()
这三个方法,他们用于生成实例包括FormControl、FormGroup、FromArray。
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) { }}
Creating dynamic forms
FormArray是用于管理任何数量的未命名控件的FormGroup的替代方法,可以动态的插入或删除,所以不知道子值的数量时可以使用FormArray来代替FormGroup。
Import the FormArray class.Define a FormArray control.使用get()访问FormArray control.Display the form array in a template.Import the FormArray class
import { FormArray } from '@angular/forms';
Define a FormArray control
通过在数组中定义控件,可以使用从零到许多的任意数量的控件来初始化表单数组,使用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('') ])});
Access the FormArray control
可以使用getter
可以轻鬆访问表单阵列的实例的别名。
get aliases() { return this.profileForm.get('aliases') as FormArray;}
还可以使用FormArray.push()
将表单数据动态的插入到现有表单中。
addAlias() { this.aliases.push(this.fb.control(''));}
Display the form array in the template
<div formArrayName="aliases"> <h3>Aliases</h3> <button (click)="addAlias()">Add Alias</button> <div *ngFor="let alias of aliases.controls; let i=index"> <!-- The repeated alias template --> <label> Alias: <input type="text" [formControlName]="i"> </label> </div></div>
使用*ngFor
遍历aliases中的所有表单实例,因为表单数组元素未命名,所以您将索引分配给i变量,并将其传递给每个控件以将其绑定到formControlName输入。
Reactive forms API summary
Classes
<input>
或<select>
FormGroup管理一组AbstractControl实例的值和有效状态,它包含他这个FormGroup的子FormControl。FormArray管理AbstractControl实例的阵列的值有效性。FormBuilder用于创建FormControl的方法。Directives
参考文献:
Angular - Reactive Forms