Angular表单验证

来源:  2018-04-20 15:10:35    评论:0点击:

Angular表单验证


通过验证用户输入的准确性和完整性,来增强整体数据质量。

本文展示了如何在界面中如何验证用户输入,并显示有用的验证信息,先使用模板驱动表单方式,再使用响应式表单方式。

参见表单响应式表单了解关于这些选择的更多知识。

模板驱动验证

为了往模板驱动表单中添加验证机制,你要添加一些验证属性,就像原生的 HTML 表单验证器。 Angular 会用指令来匹配这些具有验证功能的指令。

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

你可以通过把 ngModel 导出成局部模板变量来查看该控件的状态。 比如下面这个例子就把 NgModel 导出成了一个名叫 name 的变量:


template/hero-form-template.component.html (name)

      

<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>

    

请注意以下几点:

  • <input> 元素带有一些 HTML 验证属性:requiredminlength。它还带有一个自定义的验证器指令 forbiddenName。要了解更多信息,参见自定义验证器一节。

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

  • <div> 元素的 *ngIf 揭露了一套嵌套消息 divs,但是只在有“name”错误和控制器为 dirty 或者 touched

  • 每个嵌套的 <div> 为其中一个可能出现的验证错误显示一条自定义消息。比如 requiredminlengthforbiddenName

 

为何检查 dirtytouched

你肯定不希望应用在用户还没有编辑过表单的时候就给他们显示错误提示。 对 dirtytouched 的检查可以避免这种问题。改变控件的值会改变控件的 dirty(脏)状态,而当控件失去焦点时,就会改变控件的 touched(碰过)状态。

响应式表单的验证

在响应式表单中,真正的源码都在组件类中。不应该通过模板上的属性来添加验证器,而应该在组件类中直接把验证器函数添加到表单控件模型上(FormControl)。然后,一旦控件发生了变化,Angular 就会调用这些函数。

验证器函数

有两种验证器函数:同步验证器和异步验证器。

  • 同步验证器函数接受一个控件实例,然后返回一组验证错误或 null。你可以在实例化一个 FormControl 时把它作为构造函数的第二个参数传进去。

  • 异步验证器函数接受一个控件实例,并返回一个承诺(Promise)或可观察对象(Observable),它们稍后会发出一组验证错误或者 null。你可以在实例化一个 FormControl 时把它作为构造函数的第三个参数传进去。

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

内置验证器

你可以写自己的验证器,也可以使用一些 Angular 内置的验证器。

模板驱动表单中可用的那些属性型验证器(如 requiredminlength 等)对应于 Validators 类中的同名函数。要想查看内置验证器的全列表,参见 API 参考手册中的验证器部分。

要想把这个英雄表单改造成一个响应式表单,你还是用那些内置验证器,但这次改为用它们的函数形态。


reactive/hero-form-reactive.component.ts (validator functions)

      

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)
  });
}

get name() { return this.heroForm.get('name'); }

get power() { return this.heroForm.get('power'); }

    

注意

    name 控件设置了两个内置验证器:Validators.required 和 Validators.minLength(4)。要了解更多信息,参见本章的自定义验证器一节。

    由于这些验证器都是同步验证器,因此你要把它们作为第二个参数传进去。

    可以通过把这些函数放进一个数组后传进去,可以支持多重验证器。

    这个例子添加了一些 getter 方法。在响应式表单中,你通常会通过它所属的控件组(FormGroup)的 get 方法来访问表单控件,但有时候为模板定义一些 getter 作为简短形式。

如果你到模板中找到 name 输入框,就会发现它和模板驱动的例子很相似。
reactive/hero-form-reactive.component.html (name with error msg)

      

<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 样式或可访问性。

自定义验证器

由于内置验证器无法适用于所有应用场景,有时候你还是得创建自定义验证器。

考虑前面的例子中的 forbiddenNameValidator 函数。该函数的定义看起来是这样的:
shared/forbidden-name.directive.ts (forbiddenNameValidator)

      

/** A hero's name can't match the given regular expression */
export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
  return (control: AbstractControl): {[key: string]: any} => {
    const forbidden = nameRe.test(control.value);
    return forbidden ? {'forbiddenName': {value: control.value}} : null;
  };
}

    

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

在本例中,禁止的名字是“bob”; 验证器会拒绝任何带有“bob”的英雄名字。 在其他地方,只要配置的正则表达式可以匹配上,它可能拒绝“alice”或者任何其他名字。

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

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

在响应式表单组件中,添加自定义验证器相当简单。你所要做的一切就是直接把这个函数传给 FormControl 。
reactive/hero-form-reactive.component.ts (validator functions)

      

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)
});

    

添加到模板驱动表单

在模板驱动表单中,你不用直接访问 FormControl 实例。所以不能像响应式表单中那样把验证器传进去,而应该在模板中添加一个指令。

ForbiddenValidatorDirective 指令相当于 forbiddenNameValidator 的包装器。

Angular 在验证流程中的识别出指令的作用,是因为指令把自己注册到了 NG_VALIDATORS 提供商中,该提供商拥有一组可扩展的验证器。
shared/forbidden-name.directive.ts (providers)

      

providers: [{provide: NG_VALIDATORS, useExisting: ForbiddenValidatorDirective, multi: true}]

    

然后该指令类实现了 Validator 接口,以便它能简单的与 Angular 表单集成在一起。这个指令的其余部分有助于你理解它们是如何协作的:
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} {
    return this.forbiddenName ? forbiddenNameValidator(new RegExp(this.forbiddenName, 'i'))(control)
                              : null;
  }
}

    

一旦 ForbiddenValidatorDirective 写好了,你只要把 forbiddenName 选择器添加到输入框上就可以激活这个验证器了。比如:
template/hero-form-template.component.html (forbidden-name-input)

      

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

    

你可能注意到了自定义验证器指令是用 useExisting 而不是 useClass 来实例化的。注册的验证器必须是这个 ForbiddenValidatorDirective 实例本身,也就是表单中 forbiddenName 属性被绑定到了"bob"的那个。如果用 useClass 来代替 useExisting,就会注册一个新的类实例,而它是没有 forbiddenName 的。
表示控件状态的 CSS 类

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

    .ng-valid

    .ng-invalid

    .ng-pending

    .ng-pristine

    .ng-dirty

    .ng-untouched

    .ng-touched

这个英雄表单使用 .ng-valid 和 .ng-invalid 来设置每个表单控件的边框颜色。
forms.css (status classes)

      

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

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

    
你可以运行在线例子 / 下载范例来查看完整的响应式和模板驱动表单的代码。


来源:https://www.angular.cn/guide/form-validation
为您推荐

友情链接 |九搜汽车网 |手机ok生活信息网|ok生活信息网|ok微生活
 Powered by www.360SDN.COM   京ICP备11022651号-4 © 2012-2016 版权