Angular 4.x中表单Reactive Forms详解

Angular 4.x 中有两种表单:

  • Template-Driven Forms - 模板驱动式表单 (类似于 Angular 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

<form novalidate>
 <label>
 <span>Full name</span>
 <input
 type="text"
 name="name"
 placeholder="Your full name">
 </label>
 <div>
 <label>
 <span>Email address</span>
 <input
 type="email"
 name="email"
 placeholder="Your email address">
 </label>
 <label>
 <span>Confirm address</span>
 <input
 type="email"
 name="confirm"
 placeholder="Confirm your email address">
 </label>
 </div>
 <button type="submit">Sign up</button>
</form>

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

  • 绑定 name、email、confirm 输入框的值
  • 为所有输入框添加表单验证功能
  • 显示验证异常信息
  • 表单验证失败时,不允许进行表单提交
  • 表单提交功能

User interface

// signup.interface.ts
export interface User {
 name: string;
 account: {
 email: string;
 confirm: string;
 }
}

ngModule and reactive forms

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

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

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

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

Reactive approach

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

signup-form.component.ts

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

@Component({
 selector: 'signup-form',
 template: `
 <form novalidate>...</form>
 `
})
export class SignupFormComponent {
 constructor() {}
}

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

FormControl and FormGroup

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

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

ngOnInit() {
 this.myControl = new FormControl('Semlinker');
}

2、FormGroup - 包含是一组 FormControl 实例,可用于跟踪 FormControl 组的值和验证状态,此外也提供了一系列公共API。

使用示例:

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

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

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

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

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

此时的表单结构如下:

FormGroup -> 'myGroup'
 FormControl -> 'name'
 FormControl -> 'location'

Implementing our FormGroup model

signup.interface.ts

export interface User {
 name: string;
 account: {
 email: string;
 confirm: string;
 }
}

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

FormGroup -> 'user'
 FormControl -> 'name'
 FormGroup -> 'account'
 FormControl -> 'email'
 FormControl -> 'confirm'

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

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

@Component({...})
export class SignupFormComponent implements OnInit {
 user: FormGroup;
 ngOnInit() {
 this.user = new FormGroup({
 name: new FormControl(''),
 account: new FormGroup({
 email: new FormControl(''),
 confirm: new FormControl('')
 })
 });
 }
}

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

Binding our FormGroup model

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

<form novalidate [formGroup]="user">
 <label>
 <span>Full name</span>
 <input
 type="text"
 placeholder="Your full name"
 formControlName="name">
 </label>
 <div formGroupName="account">
 <label>
 <span>Email address</span>
 <input
 type="email"
 placeholder="Your email address"
 formControlName="email">
 </label>
 <label>
 <span>Confirm address</span>
 <input
 type="email"
 placeholder="Confirm your email address"
 formControlName="confirm">
 </label>
 </div>
 <button type="submit">Sign up</button>
</form>

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

// JavaScript APIs
FormGroup -> 'user'
 FormControl -> 'name'
 FormGroup -> 'account'
 FormControl -> 'email'
 FormControl -> 'confirm'

// DOM bindings
formGroup -> 'user'
 formControlName -> 'name'
 formGroupName -> 'account'
 formControlName -> 'email'
 formControlName -> 'confirm'

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

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

Reactive submit

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

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

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

export class SignupFormComponent {
 user: FormGroup;
 onSubmit({ value, valid }: { value: User, valid: boolean }) {
 console.log(value, valid);
 }
}

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

export class SignupFormComponent {
 user: FormGroup;
 onSubmit() {
 console.log(this.user.value, this.user.valid);
 }
}

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

Reactive error validation

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

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

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

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

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

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

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

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

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

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

import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { User } from './signup.interface';

@Component({
 selector: 'signup-form',
 template: `
 <form novalidate (ngSubmit)="onSubmit(user)" [formGroup]="user">
  <label>
  <span>Full name</span>
  <input type="text" placeholder="Your full name" formControlName="name">
  </label>
  <div class="error" *ngIf="user.get('name').hasError('required') &&
   user.get('name').touched">
  Name is required
  </div>
  <div class="error" *ngIf="user.get('name').hasError('minlength') &&
   user.get('name').touched">
  Minimum of 2 characters
  </div>
  <div formGroupName="account">
  <label>
   <span>Email address</span>
   <input type="email" placeholder="Your email address" formControlName="email">
  </label>
  <div
   class="error"
   *ngIf="user.get('account').get('email').hasError('required') &&
    user.get('account').get('email').touched">
   Email is required
  </div>
  <label>
   <span>Confirm address</span>
   <input type="email" placeholder="Confirm your email address"
    formControlName="confirm">
  </label>
  <div
   class="error"
   *ngIf="user.get('account').get('confirm').hasError('required') &&
    user.get('account').get('confirm').touched">
   Confirming email is required
  </div>
  </div>
  <button type="submit" [disabled]="user.invalid">Sign up</button>
 </form>
 `
})
export class SignupFormComponent implements OnInit {
 user: FormGroup;
 constructor() {}
 ngOnInit() {
 this.user = new FormGroup({
  name: new FormControl('', [Validators.required, Validators.minLength(2)]),
  account: new FormGroup({
  email: new FormControl('', Validators.required),
  confirm: new FormControl('', Validators.required)
  })
 });
 }
 onSubmit({ value, valid }: { value: User, valid: boolean }) {
 console.log(value, valid);
 }
}

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

Simplifying with FormBuilder

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

import { FormBuilder, FormGroup, Validators } from '@angular/forms';

export class SignupFormComponent implements OnInit {
 user: FormGroup;
 constructor(private fb: FormBuilder) {}
 ...
}

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

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

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

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

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

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

@Component({...})
export class SignupFormComponent implements OnInit {
 user: FormGroup;
 constructor(private fb: FormBuilder) {}
 ngOnInit() {
 this.user = this.fb.group({
  name: ['', [Validators.required, Validators.minLength(2)]],
  account: this.fb.group({
  email: ['', Validators.required],
  confirm: ['', Validators.required]
  })
 });
 }
 onSubmit({ value, valid }: { value: User, valid: boolean }) {
 console.log(value, valid);
 }
}

我有话说

Template-Driven Forms vs Reactive Forms

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

  • 使用方便
  • 适用于简单的场景
  • 通过 [(ngModel)] 实现数据双向绑定
  • 最小化组件类的代码
  • 不易于单元测试

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

  • 比较灵活
  • 适用于复杂的场景
  • 简化了HTML模板的代码,把验证逻辑抽离到组件类中
  • 方便的跟踪表单控件值的变化
  • 易于单元测试

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • Angular2平滑升级到Angular4的步骤详解

    前言 Angular4终于在两天前发布了正式版本,那么怎么升级呢?其实Angular2和Angular4之间属于平滑过渡,并不像1和2之间颠覆性的重写代码. Angular4现已发布  http://www.jb51.net/article/109685.htm 为什么跳过Angular 3? 根据Angular团队首席开发Igor Minar的说法:随着Angular 2的发布,Angular团队引入了语义化版本控制规范,即:将语义化版本用三组数字来表示,按照major.minor.patch

  • Web前端框架Angular4.0.0 正式版发布

    前言 angular4.0.0正式版现在可以使用了.这是自我们宣布angular改版后,首次发布的专业版本.它向下兼容,支持所有使用了angular2.x.x版本的应用程序. 我们很高兴和大家分享这个版本,它包括最近的3个月中我们做的功能上的主要改进.我们努力让开发者们能够很容易的接受angular4.0.0. 新版本的特性 •更轻量化.更快 在这个新版本上,我们履行了我们的承诺,我们做到了让Angular的程序变的更轻量化,更快.但是我们还没有完全的优化完,在接下来的日子中,你会看到我们将着重

  • Angular 4.x 动态创建表单实例

    本文将介绍如何动态创建表单组件,我们最终实现的效果如下: 在阅读本文之前,请确保你已经掌握 Angular 响应式表单和动态创建组件的相关知识,如果对相关知识还不了解,推荐先阅读一下 Angular 4.x Reactive Forms和 Angular 4.x 动态创建组件 这两篇文章.对于已掌握的读者,我们直接进入主题. 创建动态表单 创建 DynamicFormModule 在当前目录先创建 dynamic-form 目录,然后在该目录下创建 dynamic-form.module.ts

  • Angular.js 4.x中表单Template-Driven Forms详解

    Angular 4.x 中有两种表单: Template-Driven Forms - 模板驱动式表单 (类似于 Angular 1.x 中的表单 ) Reactive Forms - 响应式表单 本文主要介绍 Template-Driven Forms (模板驱动式表单) ,将涉及 ngForm.ngModel.ngModelGroup.表单提交事件.表单验证和异常信息输出等内容. Contents ngModule and template-driven forms Binding ngFo

  • Angular 4.x中表单Reactive Forms详解

    Angular 4.x 中有两种表单: Template-Driven Forms - 模板驱动式表单 (类似于 Angular 1.x 中的表单 ) Reactive Forms (Model-Driven Forms) - 响应式表单 Template-Driven Forms (模板驱动表单) ,我们之前的文章已经介绍过了,了解详细信息,请查看 - Angular 4.x Template-Driven Forms. Contents ngModule and reactive forms

  • Python Django框架中表单的用法详解

    目录 文件上传 Form表单 表单字段 字段参数 widget参数 表单的验证 表单模型文件上传例子 模型表单 AJAX Django保证表单的正确显示需要添加CSRF(防止网站跨站请求伪造而默认开启的一种保护方式),在<form></form>之间添加 {% csrf_token %} 在项目settings.py中 * ‘django.middleware.csrf.CsrfViewMiddleware’, * 引入,如果没有此中间件,手动添加. 文件上传 首次打开路径是GET

  • Yii中表单用法实例详解

    本文实例讲述了Yii中表单用法.分享给大家供大家参考,具体如下: 在 Yii 中处理表单时,通常需要以下步骤: 1. 创建用于表现所要收集数据字段的模型类. 2. 创建一个控制器动作,响应表单提交. 3. 在视图脚本中创建与控制器动作相关的表单. 一.创建模型 在编写表单所需的 HTML 代码之前,我们应该先确定来自最终用户输入的数据的类型,以及这些数据应符合什么样的规则.模型类可用于记录这些信息.正如模型章节所定义的,模型是保存用户输入和验证这些输入的中心位置. 取决于使用用户所输入数据的方式

  • Angular表单验证实例详解

    表单验证 我去,我感觉我这个人其实还是一个很傻逼的一个人,老是因为拼错了一个单词或者怎么样就浪费我很长时间,这样真的不行不行,要正确对待这个问题,好了,说正题吧,angular也有表单验证minlength,maxlength,required呀这些个东西,还有也支持h5的那些验证,h5的那些验证,就是type啦,type='email',number,url呀这些,然后现在要用angular来验证,可以定义样式哈,不错,然后怎么验证呢,好的上代码 <!DOCTYPE html> <ht

  • Angular4表单验证代码详解

     背景: 最近在itoo页面调整的时候,发现页面表单或者是文本框没有做基本的判断操作,所以着手demo一篇,希望对大家有帮助!! -------------------------------------------------------------------------------- 1.创建表单组件: ng g c login1 2.1单规则验证: <label>用户名:</label> <input type="text" #userNameRe

  • 关于angular js_$watch监控属性和对象详解

    $Watch:(监听一个model,当一个model每次改变时,都会触发第二个函数) $watch('watchFn',watchAction,deepWatch) watchFn:带有Angular 表达式或者函数的字符串,它会返回被监控的数据模型的当前值. watchAction: 一个函数function(newValue,oldValue){},当watchFn 发生变化时会被调用 deepWatch:默认为false,监听数组的某个元素或者对象的属性时设置为true; 监控一个属性:

  • 基于Vue单文件组件详解

    本文将详细介绍Vue单文件组件 概述 在很多 Vue 项目中,使用 Vue.component 来定义全局组件,紧接着用 new Vue({ el: '#container '}) 在每个页面内指定一个容器元素. 这种方式在很多中小规模的项目中运作的很好,在这些项目里 JavaScript 只被用来加强特定的视图.但当在更复杂的项目中,或者前端完全由 JavaScript 驱动的时候,下面这些缺点将变得非常明显: 1.全局定义 (Global definitions) 强制要求每个 compon

  • Angular指令之restict匹配模式的详解

    Angular指令之restict匹配模式的详解 <body data-ng-app="myapp"> <runn2></runn2> <div data-runn2></div> <div class="runn2"></div> <!-- directive: runn2 --> <script> var app=angular.module("

  • java 实现单链表逆转详解及实例代码

    java 实现单链表逆转详解 实例代码: class Node { Node next; String name; public Node(String name) { this.name = name; } /** * 打印结点 */ public void show() { Node temp = this; do { System.out.print(temp + "->"); temp = temp.next; }while(temp != null); System.o

随机推荐