Angular 4.x Reactive Forms

前端之家收集整理的这篇文章主要介绍了Angular 4.x Reactive Forms前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

Angular 4.x 中有两种表单:

  • Template-Driven Forms - 模板驱动式表单 (类似于 AngularJS 1.x 中的表单 )

  • Reactive Forms (Model-Driven Forms) - 响应式表单

Template-Driven Forms (模板驱动表单) ,我们之前的文章已经介绍过了,了解详细信息,请查看 - Angular 4.x Template-Driven Forms

Contents

  • ngModule and reactive forms

  • FormControl and FormGroup

  • Implementing our FormGroup model

  • Binding our FormGroup model

  • Reactive submit

  • Reactive error validation

  • Simplifying with FormBuilder

Form base and interface

Form base

  1. <form novalidate>
  2. <label>
  3. <span>Full name</span>
  4. <input
  5. type="text"
  6. name="name"
  7. placeholder="Your full name">
  8. </label>
  9. <div>
  10. <label>
  11. <span>Email address</span>
  12. <input
  13. type="email"
  14. name="email"
  15. placeholder="Your email address">
  16. </label>
  17. <label>
  18. <span>Confirm address</span>
  19. <input
  20. type="email"
  21. name="confirm"
  22. placeholder="Confirm your email address">
  23. </label>
  24. </div>
  25. <button type="submit">Sign up</button>
  26. </form>

接下来我们要实现的功能如下:

  • 绑定 name、email、confirm 输入框的值

  • 为所有输入框添加表单验证功能

  • 显示验证异常信息

  • 表单验证失败时,不允许进行表单提交

  • 表单提交功能

User interface

  1. // signup.interface.ts
  2. export interface User {
  3. name: string;
  4. account: {
  5. email: string;
  6. confirm: string;
  7. }
  8. }

ngModule and reactive forms

在我们继续深入介绍 reactive forms 表单前,我们必须在 @NgModule 中导入 @angular/forms 库中的 ReactiveFormsModule

  1. import { ReactiveFormsModule } from '@angular/forms';
  2.  
  3. @NgModule({
  4. imports: [
  5. ...,ReactiveFormsModule
  6. ],declarations: [...],bootstrap: [...]
  7. })
  8. export class AppModule {}

友情提示:若使用 reactive forms,则导入 ReactiveFormsModule;若使用 template-driven 表单,则导入 FormsModule。

Reactive approach

我们将基于上面的定义的基础表单,创建 SignupFormComponent

signup-form.component.ts

  1. import { Component } from '@angular/core';
  2.  
  3. @Component({
  4. selector: 'signup-form',template: `
  5. <form novalidate>...</form>
  6. `
  7. })
  8. export class SignupFormComponent {
  9. constructor() {}
  10. }

这是一个基础的组件,在我们实现上述功能前,我们需要先介绍 FormControlFormGroupFormBuilder 的概念和使用。

FormControl and FormGroup

我们先来介绍一下 FormControl 和 FormGroup 的概念:

  • FormControl - 它是一个为单个表单控件提供支持的类,可用于跟踪控件的值和验证状态,此外还提供了一系列公共API。

使用示例:

  1. ngOnInit() {
  2. this.myControl = new FormControl('Semlinker');
  3. }
  • FormGroup - 包含是一组 FormControl 实例,可用于跟踪 FormControl 组的值和验证状态,此外也提供了一系列公共API。

使用示例:

  1. ngOnInit() {
  2. this.myGroup = new FormGroup({
  3. name: new FormControl('Semlinker'),location: new FormControl('China,CN')
  4. });
  5. }

现在我们已经创建了 FormControlFormGroup 实例,接下来我们来看一下如何使用:

  1. <form novalidate [formGroup]="myGroup">
  2. Name: <input type="text" formControlName="name">
  3. Location: <input type="text" formControlName="location">
  4. </form>

注意事项:Template-Driven Forms 中介绍的 ngModelname="" 属性,已经被移除了。这是一件好事,让我们的模板更简洁。

上面示例中,我们必须使用 [formGroup] 绑定我们创建的 myGroup 对象,除此之外还要使用 formControlName 指令,绑定我们创建的 FormControl 控件。此时的表单结构如下:

  1. FormGroup -> 'myGroup'
  2. FormControl -> 'name'
  3. FormControl -> 'location'

Implementing our FormGroup model

signup.interface.ts

  1. export interface User {
  2. name: string;
  3. account: {
  4. email: string;
  5. confirm: string;
  6. }
  7. }

与之对应的表单结构如下:

  1. FormGroup -> 'user'
  2. FormControl -> 'name'
  3. FormGroup -> 'account'
  4. FormControl -> 'email'
  5. FormControl -> 'confirm'

是的,我们可以创建嵌套的 FormGroup 集合!让我们更新一下组件 (不包含初始数据):

  1. import { Component,OnInit } from '@angular/core';
  2. import { FormControl,FormGroup } from '@angular/forms';
  3.  
  4. @Component({...})
  5. export class SignupFormComponent implements OnInit {
  6. user: FormGroup;
  7. ngOnInit() {
  8. this.user = new FormGroup({
  9. name: new FormControl(''),account: new FormGroup({
  10. email: new FormControl(''),confirm: new FormControl('')
  11. })
  12. });
  13. }
  14. }

如果我们想要设置初始数据,我们可以按照上述示例进行设置。通常情况下,我们通过服务端提供的 API 接口来获取表单的初始信息。

Binding our FormGroup model

现在我们已经实例化了 FormGroup 模型,是时候绑定到对应的 DOM 元素上了。具体示例如下:

  1. <form novalidate [formGroup]="user">
  2. <label>
  3. <span>Full name</span>
  4. <input
  5. type="text"
  6. placeholder="Your full name"
  7. formControlName="name">
  8. </label>
  9. <div formGroupName="account">
  10. <label>
  11. <span>Email address</span>
  12. <input
  13. type="email"
  14. placeholder="Your email address"
  15. formControlName="email">
  16. </label>
  17. <label>
  18. <span>Confirm address</span>
  19. <input
  20. type="email"
  21. placeholder="Confirm your email address"
  22. formControlName="confirm">
  23. </label>
  24. </div>
  25. <button type="submit">Sign up</button>
  26. </form>

现在 FormGroupFormControl 对象与 DOM 结构的关联信息如下:

  1. // JavaScript APIs
  2. FormGroup -> 'user'
  3. FormControl -> 'name'
  4. FormGroup -> 'account'
  5. FormControl -> 'email'
  6. FormControl -> 'confirm'
  7.  
  8. // DOM bindings
  9. formGroup -> 'user'
  10. formControlName -> 'name'
  11. formGroupName -> 'account'
  12. formControlName -> 'email'
  13. formControlName -> 'confirm'

当使用模板驱动的表单时,为了获取 f.value 表单的值,我们需要先执行 #f="ngForm" 的操作。而对于使用响应式的表单,我们可以通过以下方式,方便的获取表单的值:

  1. {{ user.value | json }} // { name: '',account: { email: '',confirm: '' }}

Reactive submit

跟模板驱动的表单一样,我们可以通过 ngSubmit 输出属性,处理表单的提交逻辑:

  1. <form novalidate (ngSubmit)="onSubmit(user)" [formGroup]="user">
  2. ...
  3. </form>

需要注意的是,我们使用 user 对象作为 onSubmit() 方法的参数,这使得我们可以获取表单对象的相关信息,具体处理逻辑如下:

  1. export class SignupFormComponent {
  2. user: FormGroup;
  3. onSubmit({ value,valid }: { value: User,valid: boolean }) {
  4. console.log(value,valid);
  5. }
  6. }

上面代码中,我们使用 Object destructuring (对象解构) 的方式,从user 对象中获取 valuevalid 属性的值。其中 value 的值,就是 user.value 的值。在实际应用中,我们是不需要传递 user 参数的:

  1. export class SignupFormComponent {
  2. user: FormGroup;
  3. onSubmit() {
  4. console.log(this.user.value,this.user.valid);
  5. }
  6. }

表单的数据绑定方式和提交逻辑已经介绍完了,是该介绍表单实际应用中,一个重要的环节 — 表单验证。

Reactive error validation

接下来我们来为表单添加验证规则,首先我们需要从 @angular/forms 中导入 Validators。具体使用示例如下:

  1. ngOnInit() {
  2. this.user = new FormGroup({
  3. name: new FormControl('',[Validators.required,Validators.minLength(2)]),account: new FormGroup({
  4. email: new FormControl('',Validators.required),confirm: new FormControl('',Validators.required)
  5. })
  6. });
  7. }

通过以上示例,我们可以看出,如果表单控制包含多种验证规则,可以使用数组声明多种验证规则。若只包含一种验证规则,直接声明就好。通过这种方式,我们就不需要在模板的输入控件中添加 required 属性。接下来我们来添加表单验证失败时,不允许进行表单提交功能

  1. <form novalidate (ngSubmit)="onSubmit(user)" [formGroup]="user">
  2. ...
  3. <button type="submit" [disabled]="user.invalid">Sign up</button>
  4. </form>

那么问题来了,我们要如何获取表单控件的验证信息?我们可以使用模板驱动表单中介绍的方式,具体如下:

  1. <form novalidate [formGroup]="user">
  2. {{ user.controls.name?.errors | json }}
  3. </form>

友情提示:?.prop 称为安全导航操作符,用于告诉 Angular prop 的值可能不存在。

此外我们也可以使用 FormGroup 对象提供的 API,来获取表单控件验证的错误信息:

  1. <form novalidate [formGroup]="user">
  2. {{ user.get('name').errors | json }}
  3. </form>

现在我们来看一下完整的代码

  1. import { Component,FormGroup,Validators } from '@angular/forms';
  2. import { User } from './signup.interface';
  3.  
  4. @Component({
  5. selector: 'signup-form',template: `
  6. <form novalidate (ngSubmit)="onSubmit(user)" [formGroup]="user">
  7. <label>
  8. <span>Full name</span>
  9. <input type="text" placeholder="Your full name" formControlName="name">
  10. </label>
  11. <div class="error" *ngIf="user.get('name').hasError('required') &&
  12. user.get('name').touched">
  13. Name is required
  14. </div>
  15. <div class="error" *ngIf="user.get('name').hasError('minlength') &&
  16. user.get('name').touched">
  17. Minimum of 2 characters
  18. </div>
  19. <div formGroupName="account">
  20. <label>
  21. <span>Email address</span>
  22. <input type="email" placeholder="Your email address" formControlName="email">
  23. </label>
  24. <div
  25. class="error"
  26. *ngIf="user.get('account').get('email').hasError('required') &&
  27. user.get('account').get('email').touched">
  28. Email is required
  29. </div>
  30. <label>
  31. <span>Confirm address</span>
  32. <input type="email" placeholder="Confirm your email address"
  33. formControlName="confirm">
  34. </label>
  35. <div
  36. class="error"
  37. *ngIf="user.get('account').get('confirm').hasError('required') &&
  38. user.get('account').get('confirm').touched">
  39. Confirming email is required
  40. </div>
  41. </div>
  42. <button type="submit" [disabled]="user.invalid">Sign up</button>
  43. </form>
  44. `
  45. })
  46. export class SignupFormComponent implements OnInit {
  47. user: FormGroup;
  48. constructor() {}
  49. ngOnInit() {
  50. this.user = new FormGroup({
  51. name: new FormControl('',account: new FormGroup({
  52. email: new FormControl('',Validators.required)
  53. })
  54. });
  55. }
  56. onSubmit({ value,valid);
  57. }
  58. }

功能是实现了,但创建 FormGroup 对象的方式有点繁琐,Angular 团队也意识到这点,因此为我们提供 FormBuilder ,来简化上面的操作。

Simplifying with FormBuilder

首先我们需要从 @angular/forms 中导入 FormBuilder

  1. import { FormBuilder,Validators } from '@angular/forms';
  2.  
  3. export class SignupFormComponent implements OnInit {
  4. user: FormGroup;
  5. constructor(private fb: FormBuilder) {}
  6. ...
  7. }

然后我们使用 FormBuilder 对象提供的 group() 方法,来创建 FormGroupFormControl 对象:

调整前的代码 (未使用FormBuilder):

  1. ngOnInit() {
  2. this.user = new FormGroup({
  3. name: new FormControl('',Validators.required)
  4. })
  5. });
  6. }

调整后的代码 (使用FormBuilder):

  1. ngOnInit() {
  2. this.user = this.fb.group({
  3. name: ['',Validators.minLength(2)]],account: this.fb.group({
  4. email: ['',Validators.required],confirm: ['',Validators.required]
  5. })
  6. });
  7. }

对比一下调整前和调整后的代码,是不是感觉一下子方便了许多。此时更新完后完整的代码如下:

  1. @Component({...})
  2. export class SignupFormComponent implements OnInit {
  3. user: FormGroup;
  4. constructor(private fb: FormBuilder) {}
  5. ngOnInit() {
  6. this.user = this.fb.group({
  7. name: ['',account: this.fb.group({
  8. email: ['',Validators.required]
  9. })
  10. });
  11. }
  12. onSubmit({ value,valid);
  13. }
  14. }

我有话说

Template-Driven Forms vs Reactive Forms

Template-Driven Forms (模板驱动表单) 的特点

  • 使用方便

  • 适用于简单的场景

  • 通过 [(ngModel)] 实现数据双向绑定

  • 最小化组件类的代码

  • 不易于单元测试

Reactive Forms (响应式表单) 的特点

  • 比较灵活

  • 适用于复杂的场景

  • 简化了HTML模板的代码,把验证逻辑抽离到组件类中

  • 方便的跟踪表单控件值的变化

  • 易于单元测试

参考资源

猜你在找的Angularjs相关文章