欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Angular 4.x中表单Reactive Forms详解

程序员文章站 2022-06-03 14:46:37
angular 4.x 中有两种表单: template-driven forms - 模板驱动式表单 (类似于 angular 1.x 中的表单 ) re...

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模板的代码,把验证逻辑抽离到组件类中
  • 方便的跟踪表单控件值的变化
  • 易于单元测试

总结

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