Angular 表单验证

2023-11-08

模板驱动验证

使用模板驱动验证需要依赖于原生的HTML表单验证器 Angular 会用指令来匹配具有验证功能的这些属性。

原生的HTMl验证器主要分两种

  1. 通过语义类型来进行定义
  2. 通过验证相关的属性来进行定义

语义类型

Input type Constraint description Associated violation
<input type="URL"> The value must be an absolute URL, as defined in the URL Living Standard. TypeMismatch constraint violation
<input type="email"> The value must be a syntactically valid email address, which generally has the format username@hostname.tld. TypeMismatch constraint violation

 验证相关属性

Attribute Input types supporting the attribute Possible values Constraint description Associated violation
pattern text, search, url, tel, email, password A JavaScript regular expression (compiled with the ECMAScript 5 global, ignoreCase, and multiline flags disabled) The value must match the pattern. patternMismatch constraint violation
min range, number A valid number The value must be greater than or equal to the value. rangeUnderflow constraint violation
date, month, week A valid date
datetime, datetime-local, time A valid date and time
max range, number A valid number The value must be less than or equal to the value rangeOverflow constraint violation
date, month, week A valid date
datetime, datetime-local, time A valid date and time
required text, search, url, tel, email, password, date, datetime, datetime-local, month, week, time, number, checkbox, radio, file; also on the <select> and <textarea> elements none as it is a Boolean attribute: its presence means true, its absence means false There must be a value (if set). valueMissing constraint violation
step date An integer number of days Unless the step is set to the any literal, the value must be min + an integral multiple of the step. stepMismatch constraint violation
month An integer number of months
week An integer number of weeks
datetime, datetime-local, time An integer number of seconds
range, number An integer
minlength text, search, url, tel, email, password; also on the <textarea> element An integer length The number of characters (code points) must not be less than the value of the attribute, if non-empty. All newlines are normalized to a single character (as opposed to CRLF pairs) for <textarea>. tooShort constraint violation
maxlength text, search, url, tel, email, password; also on the <textarea> element An integer length The number of characters (code points) must not exceed the value of the attribute. tooLong constraint violation

 

 每当表单控件中的值发生变化时,Angular就会进行验证,并生成一个验证错误的列表(对应着INVALID状态)或者null(对应着VALID状态);

可以通过吧ngModel导出成局部模板变量来查看控件的状态,比如像下面这样

<input id="name" name="name" class="form-control" required minlength="4" appForbiddenName="bob"
      [(ngModel)]="hero.name" #name="ngModel" >

<div *ngIf="name.invalid && (name.dirty || name.touched)"
    class="alert alert-danger">

  <div *ngIf="name.errors.required">
    Name is required.
  </div>
  <div *ngIf="name.errors.minlength">
    Name must be at least 4 characters long.
  </div>
  <div *ngIf="name.errors.forbiddenName">
    Name cannot be Bob.
  </div>
</div>

 #name="ngModel"NgModel 导出成了一个名叫 name 的局部变量。NgModel 把自己控制的 FormControl 实例的属性映射出去,让你能在模板中检查控件的状态,比如 validdirty。要了解完整的控件属性,参见 API 参考手册中的AbstractControl

响应式表单的验证

响应式表单控制的源头在组件类,就不能通过模板上的属性来添加验证器了,而是直接在组件类中直接把验证器函数添加到表单控件模型(FormControl)上。当控件发生变化的时候就会调用这些函数。

ngOnInit(): void {
  this.heroForm = new FormGroup({
    'name': new FormControl(this.hero.name, [
      Validators.required,
      Validators.minLength(4),
      forbiddenNameValidator(/bob/i) // <-- Here's how you pass in the custom validator.
    ]),
    'alterEgo': new FormControl(this.hero.alterEgo),
    'power': new FormControl(this.hero.power, Validators.required)
  });

}
<input id="name" class="form-control" formControlName="name" required >

<div *ngIf="name.invalid && (name.dirty || name.touched)"
    class="alert alert-danger">

  <div *ngIf="name.errors.required">
    Name is required.
  </div>
  <div *ngIf="name.errors.minlength">
    Name must be at least 4 characters long.
  </div>
  <div *ngIf="name.errors.forbiddenName">
    Name cannot be Bob.
  </div>
</div>
  • 该表单不再导出任何指令,而是使用组件类中定义的 name 读取器。
  • required 属性仍然存在,虽然验证不再需要它,但你仍然要在模板中保留它,以支持 CSS 样式或可访问性。 

从验证过程上看有两种验证器函数:同步验证器和异步验证器。

  • 同步验证器函数接受一个控件实例,然后返回一组验证错误或null,可以在实例化FormControl的时候把函数作为构造函数的第二个参数传递进去。
  • 异步验证器函数同样也接收一个控件实例,并返回一个承诺(Promise)或可观察对象(Observable),最终函数会返回一组验证错误或者null,可以在实例化FormControl的时候把她当做构造函数的第三个函数传递进去。

出于性能方面的考虑,只有在所有同步验证器都通过之后,Angular 才会运行异步验证器。当每一个异步验证器都执行完之后,才会设置这些验证错误。

从验证器来源来看也有两种验证器:内置验证器和自定义验证器

  • 内置验证器与表单中使用的验证器类似,Validators都对应实现了其同名函数,具体如下 详见API
class Validators {
  static min(min: number): ValidatorFn
  static max(max: number): ValidatorFn
  static required(control: AbstractControl): ValidationErrors | null
  static requiredTrue(control: AbstractControl): ValidationErrors | null
  static email(control: AbstractControl): ValidationErrors | null
  static minLength(minLength: number): ValidatorFn
  static maxLength(maxLength: number): ValidatorFn
  static pattern(pattern: string | RegExp): ValidatorFn
  static nullValidator(control: AbstractControl): ValidationErrors | null
  static compose(validators: ValidatorFn[]): ValidatorFn | null
  static composeAsync(validators: AsyncValidatorFn[]): AsyncValidatorFn | null
}  
  • 自定义验证器:DEMO 如下,写在一起就能达到即给模板使用又给组件类使用的目的。
// 声明成指令给模板验证用 shared/forbidden-name.directive.ts (directive) 
@Directive({
  selector: '[appForbiddenName]',
  providers: [{provide: NG_VALIDATORS, useExisting: ForbiddenValidatorDirective, multi: true}]
})
export class ForbiddenValidatorDirective implements Validator {
  @Input('appForbiddenName') forbiddenName: string;

  validate(control: AbstractControl): {[key: string]: any} | null {
    return this.forbiddenName ? forbiddenNameValidator(new RegExp(this.forbiddenName, 'i'))(control) : null;
  }
}
// 定义函数给验证方法用
export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
  return (control: AbstractControl): {[key: string]: any} | null => {
    const forbidden = nameRe.test(control.value);
    return forbidden ? {'forbiddenName': {value: control.value}} : null;
  };
}

这个函数实际上是一个工厂,它接受一个用来检测指定名字是否已被禁用的正则表达式,并返回一个验证器函数。

forbiddenNameValidator 工厂函数返回配置好的验证器函数。 该函数接受一个 Angular 控制器对象,并在控制器值有效时返回 null,或无效时返回验证错误对象。 验证错误对象通常有一个名为验证秘钥(forbiddenName)的属性。其值为一个任意词典,你可以用来插入错误信息({name})。

自定义异步验证器和同步验证器很像,只是它们必须返回一个稍后会输出 null 或“验证错误对象”的承诺(Promise)或可观察对象,如果是可观察对象,那么它必须在某个时间点被完成(complete),那时候这个表单就会使用它输出的最后一个值作为验证结果。(译注:HTTP 服务是自动完成的,但是某些自定义的可观察对象可能需要手动调用 complete 方法)

表示控件状态的CSS类

Angular 会自动把很多控件属性作为 CSS 类映射到控件所在的元素上。你可以使用这些类来根据表单状态给表单控件元素添加样式。目前支持下列类:

.ng-valid

.ng-invalid

.ng-pending

.ng-pristine

.ng-dirty

.ng-untouched

.ng-touched

.ng-valid[required], .ng-valid.required  {
  border-left: 5px solid #42A948; /* green */
}


.ng-invalid:not(form)  {
  border-left: 5px solid #a94442; /* red */
}

跨字段交叉验证

除了单独的控件验证之外,有时候还需要多控件的联合验证,这个时候就需要用到跨字段的交叉验证方式。先粘代码

  • 这个身份验证器实现了 ValidatorFn 接口。它接收一个 Angular 表单控件对象作为参数,当表单有效时,它返回一个 null,否则返回 ValidationErrors 对象。
  • 我们先通过调用 FormGroupget 方法来获取子控件。然后,简单地比较一下 namealterEgo 控件的值。
// 先定义指令给模板验证使用,指令调用导出的验证方法
@Directive({
  selector: '[appIdentityRevealed]',
  providers: [{ provide: NG_VALIDATORS, useExisting: IdentityRevealedValidatorDirective, multi: true }]
})
export class IdentityRevealedValidatorDirective implements Validator {
  validate(control: AbstractControl): ValidationErrors {
    return identityRevealedValidator(control)
  }
}
// 下面的方法给指令使用也可以给组件类使用
export const identityRevealedValidator: ValidatorFn = (control: FormGroup): ValidationErrors | null => {
  const name = control.get('name');
  const alterEgo = control.get('alterEgo');

  return name && alterEgo && name.value === alterEgo.value ? { 'identityRevealed': true } : null;
};

 响应式表单验证:需要在创建FormGroup时把一个新的验证器传给他的第二个参数.

const heroForm = new FormGroup({
  'name': new FormControl(),
  'alterEgo': new FormControl(),
  'power': new FormControl()
}, { validators: identityRevealedValidator });

 模板驱动表单验证:我们要把该指令添加到 HTML 模板中。由于验证器必须注册在表单的最高层,所以我们要把该指令放在 form 标签上。

<form #heroForm="ngForm" appIdentityRevealed>

<!-- 错误输出-->
<div *ngIf="heroForm.errors?.identityRevealed && (heroForm.touched || heroForm.dirty)" 
class="cross-validation-error-message alert alert-danger">
    Name cannot match alter ego.
</div>

异步验证器

就像同步验证器有 ValidatorFnValidator 接口一样,异步验证器也有自己对应的接口:AsyncValidatorFnAsyncValidator

它们非常像,但是有下列不同:

  • 它们必须返回承诺(Promise)或可观察对象(Observable),

  • 返回的可观察对象必须是有限的,也就是说,它必须在某个时间点结束(complete)。要把无尽的可观察对象转换成有限的,可以使用 firstlasttaketakeUntil 等过滤型管道对其进行处理。

注意!异步验证总是会在同步验证之后执行,并且只有当同步验证成功了之后才会执行。如果更基本的验证方法已经失败了,那么这能让表单避免进行可能会很昂贵的异步验证过程,比如 HTTP 请求。

在异步验证器开始之后,表单控件会进入 pending 状态。你可以监视该控件的 pending 属性,利用它来给用户一些视觉反馈,表明正在进行验证。

常见的 UI 处理模式是在执行异步验证时显示一个旋转指示标(spinner)。下面的例子展示了在模板驱动表单中该怎么做:

<input [(ngModel)]="name" #model="ngModel" appSomeAsyncValidator>
<app-spinner *ngIf="model.pending"></app-spinner>

实现自定义验证器

@Injectable({ providedIn: 'root' })
export class UniqueAlterEgoValidator implements AsyncValidator {
  constructor(private heroesService: HeroesService) {}

  validate(
    ctrl: AbstractControl
  ): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {
    return this.heroesService.isAlterEgoTaken(ctrl.value).pipe(
      map(isTaken => (isTaken ? { uniqueAlterEgo: true } : null)),
      catchError(() => of(null))
    );
  }
}
// HeroesService 负责向英雄数据库发起一个 HTTP 请求
interface HeroesService {
  isAlterEgoTaken: (alterEgo: string) => Observable<boolean>;
}

当验证开始的时候,UniqueAlterEgoValidator 把任务委托给 HeroesServiceisAlterEgoTaken() 方法,并传入当前控件的值。这时候,该控件会被标记为 pending 状态,直到 validate() 方法所返回的可观察对象完成(complete)了。

isAlterEgoTaken() 方法会发出一个 HTTP 请求,并返回一个 Observable<boolean> 型结果。我们通过 map 操作符把响应对象串起来,并把它转换成一个有效性结果。 与往常一样,如果表单有效则返回 null,否则返回 ValidationErrors。我们还是用 catchError 操作符来确保对任何潜在错误都进行了处理。

这里,我们决定将 isAlterEgoTaken() 中的错误视为成功验证。你也可以将其视为失败,并返回 ValidationError 对象。

一段时间之后,可观察对象完成了,异步验证也就结束了。这时候 pending 标志就改成了 false,并且表单的有效性也更新了。

性能上的注意事项

默认情况下,每当表单值变化之后,都会执行所有验证器。对于同步验证器,没有什么会显著影响应用性能的地方。不过,异步验证器通常会执行某种 HTTP 请求来对控件进行验证。如果在每次按键之后都发出 HTTP 请求会给后端 API 带来沉重的负担,应该尽量避免。

我们可以把 updateOn 属性从 change(默认值)改成 submitblur 来推迟表单验证的更新时机。

对于模板驱动表单:

<input [(ngModel)]="name" [ngModelOptions]="{updateOn: 'blur'}">

对于响应式表单:

new FormControl('', {updateOn: 'blur'});

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Angular 表单验证 的相关文章

随机推荐

  • Filter(过滤器)

    文章目录 1 Filter 的基本作用 2 Filter 的基本使用 3 Filter 的执行流程 4 Filter 执行需要注意的细节 4 1 拦截路径的配置 4 2 拦截路链 过滤器链 5 案例小结 登录验证 6 小结 1 Filter
  • 最近5年183个Java面试问题列表及答案[最全]

    Java 面试随着时间的改变而改变 在过去的日子里 当你知道 String 和 StringBuilder 的区别 String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象 因此在每次对 St
  • Java使用itext 生成PDF,以生成个人简历为例

    在日常的应用中经常会有需求 生成pdf或者word文档 常见的第三方工具有Apache的poi 以及itext等等 这里使用itext生成pdf 以个人简历作为示例 了解itextde 使用 第0步 效果 第一步 引入依赖包
  • Latent Dirichlet Allocation(LDA)主题模型理论

    LDA是给文本建模的一种方法 属于生成模型 生成模型是指该模型可以随机生成可观测的数据 LDA可以随机生成一篇由N个主题组成的文章 通过对文本的建模 可以对文本进行主题分类 判断相似度等 LDA通过将文本映射到主题空间 即认为一篇文章有若干
  • python嵌套列表转为字典_python如何把列表变字典

    现在有一个列表 list1 key1 key2 key3 把他转为这样的字典 key1 1 key2 2 key3 3 python将列表变字典的两种方法 1 方法 再构造一个列表list2 1 2 3 使用zip转换为元组后 在将元组转换
  • angular原理及模块简介

    本人前端小白 奈何在公司在做一个PC端的程序 用angular写 不得不自学了一下angular框架 虽然在工作过程中勉强勉强够了 但是觉得既然用了就稍微了解得全面一点 所以花了几个晚上看了一下angular的developer guide
  • Mysql 参数autoReconnect=true 解决8小时连接失效

    1 即使在创建Mysql时url中加入了autoReconnect true参数 一但这个连接两次访问数据库的时间超出了服务器端wait timeout的时间限制 还是会CommunicationsException The last pa
  • SIEBEL功能组件,eScript入门

    文章目录 前言 学习任务 一 MVG的配置与学习 1 1 M的MVG配置 以统计订单行数量功能为例 1 M M的MVG配置 二 View Mode Visibility配置 1 Personal安全性 2 Position安全性 三 EBC
  • anguarjs 上传图片预览_轻量级 Vue 图片上传组件V-Uploader

    今天给小伙伴们推荐一款超好用的Vue图片上传组件VUploader v uploader 基于vue2 x构建的简单易上手的图片上传组件 支持单张 多张图片 缩略图预览及拖拽上传功能 安装 npm i v uploader S 引入插件 i
  • gitlab迁移+升级

    一 目的 gitlab9 4 3迁移并升级到gitlab11 8 1 二 环境 原gitlab的版本为9 4 3 仓库位置已更改为 apps01 gitlab 三 操作步骤 迁移gitlab9 4 3 在新机器上安装好与原gitlab相同版
  • 【设计模式】外观模式

    外观模式 Facade Pattern 隐藏系统的复杂性 并向客户端提供了一个客户端可以访问系统的接口 这种类型的设计模式属于结构型模式 它向现有的系统添加一个接口 来隐藏系统的复杂性 这种模式涉及到一个单一的类 该类提供了客户端请求的简化
  • 数据库学习笔记_3_ER模型详解_约束

    一个ER模型必然需要对于其内容做出约束 接下来我们会从mapping cardinality 和participation constraints keys讲起 mapping cardinalities cardinality 字面意义是
  • python中[-1]、[:]、[:-1]、[::-1]、[::-2]、[2::]、[2::-1]用法

    a 0 1 2 3 4 5 6 7 8 9 10 11 12 a 1 列表a的最后一个元素 12 a 列表a的从0号元素到最后一个元素 0 1 2 3 4 5 6 7 8 9 10 11 12 a 1 列表a的从0号元素到倒数第二个元素 不
  • Centos7设置静态IP

    sudo vi etc sysconfig network scripts ifcfg
  • Docker部署Elasticsearch+Kibana

    方式1 编写docker compose yml version 3 7 services elasticsearch image elasticsearch 7 10 1 container name elasticsearch 001
  • AMOP 实践 (普通话题)

    普通话题不需要额外创建 消息的发布直接根据 AmopMsgOut 的setTopic 方法设置 本次消息发布的 topic 本次示例为 Java SDK 的使用测试 且使用代码完成 Topic 的创建以及订阅 1 订阅话题 1 1 创建一个
  • 多模态信息检索流程,多模态信息检索的商业应用,多模态信息检索涵盖技术与未来发展

    多模态信息检索流程 1 收集不同类型的信息 如文本 图像 音频 视频等 2 对不同类型的信息进行处理和识别 如图像识别 语音识别等 3 将不同类型的信息转化为统一的表示形式 如向量表示或语义空间表示 4 进行信息匹配和检索 比较不同类型的信
  • servlet的会话管理

    在Servlet规范中 Servlet的会话管理技术主要有4种 URL重写 Cookie 隐藏表单域以及HttpSession 在Java Servlet API中 javax servlet http HttpSession接口封装了Se
  • 异星工厂服务器资源修改,异星工厂存档怎么改到别的地方 异星工厂存档位置修改方法_游侠网...

    异星工厂存档怎么改到别的地方想必有些小伙伴还不是很清楚的吧 所以呢今天小编就为大家带来了异星工厂存档位置修改方法 一起来了解一下吧 异星工厂存档位置修改方法 首先我们找到 Factorio异星工厂的安装目录 然后 找到一个叫做config
  • Angular 表单验证

    模板驱动验证 使用模板驱动验证需要依赖于原生的HTML表单验证器 Angular 会用指令来匹配具有验证功能的这些属性 原生的HTMl验证器主要分两种 通过语义类型来进行定义 通过验证相关的属性来进行定义 语义类型 Input type C