模板驱动验证

使用模板驱动验证需要依赖于原生的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): ValidatorFnstatic max(max: number): ValidatorFnstatic required(control: AbstractControl): ValidationErrors | nullstatic requiredTrue(control: AbstractControl): ValidationErrors | nullstatic email(control: AbstractControl): ValidationErrors | nullstatic minLength(minLength: number): ValidatorFnstatic maxLength(maxLength: number): ValidatorFnstatic pattern(pattern: string | RegExp): ValidatorFnstatic nullValidator(control: AbstractControl): ValidationErrors | nullstatic compose(validators: ValidatorFn[]): ValidatorFn | nullstatic 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 对象。
  • 我们先通过调用 FormGroup 的 get 方法来获取子控件。然后,简单地比较一下 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'});

Angular 表单验证相关推荐

  1. Angular 表单验证类库 ngx-validator 1.0 正式发布

    背景介绍 之前写了一篇 <如何优雅的使用 Angular 表单验证>,结尾处介绍了统一验证反馈的类库 ngx-validator ,由于这段时间一直在新模块做微前端以及相关业务组件库,工具 ...

  2. 如何优雅的使用 Angular 表单验证

    随便说说,这一节可以跳过 去年参加 ngChine 2018 杭州开发者大会的时候记得有人问我: Worktile 是什么时候开始使用 Angular 的,我说是今年(2018年) 3 月份开始在新模 ...

  3. angular js创建表单_如何优雅的使用 Angular 表单验证

    随便说说,这一节可以跳过 去年参加 ngChine 2018 杭州开发者大会的时候记得有人问我: Worktile 是什么时候开始使用 Angular 的,我说是今年(2018年) 3 月份开始在新模 ...

  4. angular表单验证 1

    表单验证 通常,我们都需要对用户的表单输入做验证,以保证数据的整体质量. Angular也有两种验证表单的形式: 使用属性验证,用于模板驱动表单: 使用验证器函数进行验证,用于响应式表单. 验证器(V ...

  5. angularjs表单验证_AngularJS表单验证

    angularjs表单验证 We have updated this article for Angular 1.3 and the new ng-touched feature. 我们已经为Angu ...

  6. Angular响应式表单及表单验证

    1. 什么是响应式表单? 响应式表单提供了一种模型驱动的方式来处理表单输入,其中的值会随时间而变化. 响应式表单使用显示的,不可变的方式,管理表单在特定时间点上的状态.对表单状态的每一次变更都会返回一 ...

  7. Angular JS 中的内置方法之表单验证

    angular js 结合html5 可以实现强大的表单验证功能 关闭html5自带的表单验证功能可以用 转载于:https://www.cnblogs.com/Pikzas/p/9005861.ht ...

  8. angular学习的一些小笔记(中)之表单验证

    表单验证 我去,我感觉我这个人其实还是一个很傻逼的一个人,老是因为拼错了一个单词或者怎么样就浪费我很长时间,这样真的不行不行,要正确对待这个问题,好了,说正题吧,angular也有表单验证minlen ...

  9. angular中的表单验证

    angular中的表单验证很强大, 一共有5中验证信息,$valid,$invalid,$pristine,$dirty,$error. $valid-----当验证通过的时候,为true,不通过的时 ...

最新文章

  1. vim windows linux文件格式转换
  2. 2018-3-12论文(非结构网络中有价值信息数据挖掘)笔记二-----作者:关联规则的非结构网络有价值信息数据挖掘(看不懂,看不懂)
  3. kotlin中Int Byte的相互转换,常用
  4. Winform中选取指定文件夹并获取其下所有文件
  5. Linux下RabbitMQ服务器搭建
  6. github注册之后更新教程
  7. 设计模式(十一):从文Finder中认识组合模式(Composite Pattern)
  8. some daily
  9. base-64 字符串中的无效字符。_查找字符串中连续不重复最长字符串和长度的方法...
  10. Spring+hibernate+JSP实现Piano的数据库操作---1.目录结构+展示
  11. 条件运算符的嵌套问题
  12. 可靠的Windows版Redis
  13. [黑客入门]花无涯新手入门教程笔记
  14. 《COMPLETE MAYA PRO GRAMMIN G VOLUME II》导言
  15. springboot-1-搭建一个springboot项目
  16. switch游戏服务器设置
  17. 阿里面试官Redis把我问到哑口无言…
  18. c语言 函数 引用调用,C 引用方式调用函数
  19. 区块链的硬分叉和软分叉
  20. Python基础知识笔记

热门文章

  1. at89c2051 定时器用法 c语言编程资料,用AT89C2051单片机制作的厨房定时器
  2. 服务器虚拟化破解,VMware vSphere 7 虚拟化组件合集
  3. 计算机网络采用哪种,计算机网络有哪几种 四种计算机网络类型
  4. PyCharm 升级pip或者其他软件包的方法(简单版)
  5. ASEMI肖特基二极管MBR20200FCT特征,MBR20200FCT应用
  6. python填充红色_opencv之颜色过滤只留下图片中的红色区域操作
  7. vue路由组件强制刷新amp;amp;缓存更新
  8. UNITY实战进阶-科大讯飞TTS(离线语音合成) unity播放PCM格式的wav(不依赖第三方库)-7
  9. js 字符串比较大小
  10. AES加解密之ECB、CBC和CFB三种模式