如何进行数据验证(validator)

如何进行数据验证(validator),第1张

Angular 中有两种表单:

Template-Driven Forms - 模板驱动式表单 (类似于 AngularJS 1.x 中的表单 )

Reactive Forms - 响应式表单

我们主要使用响应式表单来进行validator方法验证

首先在 @NgModule 中导入 @angular/forms 库中的 ReactiveFormsModule:

app.module.ts

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

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

友情提示:若使用响应式表单,则导入 ReactiveFormsModule。若使用模板驱动式表单,则导入 FormsModule 。

然后创建好需要的组件:在控制台输入 ng g component 组件名 可快速创建组件并自动注册

在终端中创建组件$ ng g component Validator

在app.component.html文件中写入所创建的组件

src\app\validator\validator.component.ts

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

@Component({
  selector: 'app-validator',
  templateUrl: './validator.component.html',
  styleUrls: ['./validator.component.css']
})
export class ValidatorComponent implements OnInit {

  contactForm: FormGroup = new FormGroup({
    name: new FormControl(
      "",
      [
        Validators.required,
        Validators.minLength(2)
      ]
  })

  onSubmit() {
    console.log(this.contactForm.valid);
  }

  get name() {
    return this.contactForm.get("name")
  }

  constructor() { }

  ngOnInit(): void {
  }

}

在上述例子中,name 控件设置了两个内置验证器 Validators.required 和 Validators.minLength(2)

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

src\app\validator\validator.component.html

name?.errors控件值是否验证错误

控件状态检测,Angular会自动根据控件状态加上相应的class,如果我们需要编辑input标签在不同状态下的样式,只需要在css里写相应的类就可以了。

状态

true

false

控件是否被访问过

touched

untouched

控件值是否已经变化

dirty

pristine

控件值是否有效

valid

invalid

验证器(Validator)函数

验证器函数可以是同步函数,也可以是异步函数。

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

异步验证器 :这些异步函数接受一个控件实例并返回一个 Promise 或 Observable,它稍后会发出一组验证错误或 null。在实例化 FormControl 时,可以把它们作为第三个参数传入。

自定义同步验证器

在app文件夹下新建myValidator.ts(文件名任意.ts)文件.

src\app\myValidator.ts

import { AbstractControl, ValidationErrors, ValidatorFn } from "@angular/forms"

export class MyValidators {
    static cannotContainSpace(
        control: AbstractControl
    ): ValidationErrors | null {
        if (/\s/.test(control.value)) {
            return {
                cannotContainSpace: true
            }
        }
        return null;
    };
    static forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
        return (control: AbstractControl): { [key: string]: any } | null => {
            const forbidden = nameRe.test(control.value);
            return forbidden ? { 'forbiddenName': { value: control.value } } : null;
        };
    }

}

自定义验证器的类型是TypeScript类,类中包含具体的验证方法,验证方法必须为静态方法。

cannotContainSpace验证方法有一个参数control,类型为AbstractControl,其实就是FormControl类的实例对象的类型。如果验证成功,返回null;如果验证失败,返回对象,对象中的属性即为验证标识,值为true,标识该项验证失败。验证方法的返回值为ValidationErrors | null。

forbiddenNameValidator验证方法实际上是一个工厂,它接受一个用来检测指定名字是否已被禁用的正则表达式,并返回一个验证器函数。工厂函数返回配置好的验证器函数。 该函数接受一个 Angular 控制器对象,并在控制器值有效时返回 null,或无效时返回验证错误对象。 验证错误对象通常有一个名为验证秘钥(forbiddenName)的属性。其值为一个任意词典,你可以用来插入错误信息({name})。

src\app\validator\validator.component.ts

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

@Component({
  selector: 'app-validator',
  templateUrl: './validator.component.html',
  styleUrls: ['./validator.component.css']
})
export class ValidatorComponent implements OnInit {

  contactForm: FormGroup = new FormGroup({
    name: new FormControl(
      "",
      [
        Validators.required,
        Validators.minLength(2),
        MyValidators.cannotContainSpace,
        MyValidators.forbiddenNameValidator(/bob/i)
      ]
  })

  onSubmit() {
    console.log(this.contactForm.valid);
  }

  get name() {
    return this.contactForm.get("name")
  }


  constructor() { }

  ngOnInit(): void {
  }

}

src\app\validator\validator.component.html

自定义异步验证器

src\app\myValidator.ts

import { AbstractControl, ValidationErrors, ValidatorFn } from "@angular/forms"

export class MyValidators {
    static cannotContainSpace(
        control: AbstractControl
    ): ValidationErrors | null {
        if (/\s/.test(control.value)) {
            return {
                cannotContainSpace: true,
            }
        }
        return null;
    };
    static forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
        return (control: AbstractControl): { [key: string]: any } | null => {
            const forbidden = nameRe.test(control.value);
            return forbidden ? { 'forbiddenName': { value: control.value } } : null;
        };
    };
    static shouldBeUnique(control: AbstractControl): Promise {
        return new Promise(resolve => {
            setTimeout(() => {
                if (control.value == "admin") {
                    resolve({ shouldBeUnique: true })
                } else {
                    resolve(null)
                }
            }, 2000);
        })
    };

}

该校验器会实现一个shouldBeUnique()方法,返回结构为Promise<> 或者 Observable<> ,control.value 为当前控件的值,如果值为admin,则验证不通过返回错误对象,否则返回null。以上是一个最简单的异步验证器。

异步验证器 validate() 函数必须返回一个 Promise 或可观察对象。验证器函数接受一个control,然后返回一组错误对象(验证不通过)或 null(验证通过),当未返回任何内容时表示未开始校验。

异步校验器要求返回Promise或Observable,同时返回的可观察对象必须是有限的,也就是说,它必须在某个时间点结束(complete)。要把无尽的可观察对象转换成有限的,可以使用 first、last、take 或 takeUntil 等过滤型管道对其进行处理。

验证错误是一个对象,对象结构唯一的要求是key必须为字符串,值可以为any类型,例如你可以返回一个{duplicate: true},表示当前control的duplicate校验未通过。

值得注意的是,出于性能考虑,异步校验器会在所有同步校验器完成之后才会触发,在此之前异步校验器会处于正确状态。

异步验证开始之后,表单控件就会进入 pending 状态,当验证结果返回之后才会变成 valid 或 invalid。可以检查控件的 pending 属性,并用它来给出对验证中的视觉反馈。

src\app\validator\validator.component.ts

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

@Component({
  selector: 'app-validator',
  templateUrl: './validator.component.html',
  styleUrls: ['./validator.component.css']
})
export class ValidatorComponent implements OnInit {

  contactForm: FormGroup = new FormGroup({
    name: new FormControl(
      "",
      [
        Validators.required,
        Validators.minLength(2),
        MyValidators.cannotContainSpace,
        MyValidators.forbiddenNameValidator(/bob/i)
      ],
      MyValidators.shouldBeUnique)
  })

  onSubmit() {
    console.log(this.contactForm.valid);
  }

  get name() {
    return this.contactForm.get("name")
  }


  constructor() { }

  ngOnInit(): void {
  }

}

src\app\validator\validator.component.html

实例:

import { Inject, Injectable } from "@angular/core";
import { AbstractControl, AsyncValidator, ValidationErrors } from "@angular/forms"
import { catchError, map, Observable, of } from "rxjs";
import { NameService } from "./name.service";

@Injectable({
    providedIn: 'root'
})

export class nameValidator implements AsyncValidator{
    constructor(@Inject(String) private nameService: NameService) { }

    validate(control: AbstractControl): Promise | Observable {
        //进入管道进行串行 *** 作
         return this.nameService.isAlterEgoTaken(control.value).pipe(
            //对返回值进行处理,null表示正确,对象表示错误
            map(isTaken => (isTaken ? { uniqueAlterEgo: true } : null)),
            //处理任何潜在的错误
            catchError(() => of(null))
        );
    }
}

interface NameService {
    isAlterEgoTaken: (alterEgo: string) => Observable;
}

在真实的应用中,NameService 会负责向数据库发起一个 HTTP 请求,以检查该数据是否可用。 从该验证器的视角看,此服务的具体实现无关紧要,所以这个例子仅仅针对 NameService 接口来写实现代码。

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

isAlterEgoTaken() 方法会调度一个 HTTP 请求来检查第二人格是否可用,并返回 Observable 作为结果。validate() 方法通过 map *** 作符来对响应对象进行管道化处理,并把它转换成验证结果。

与任何验证器一样,如果表单有效,该方法返回 null,如果无效,则返回 ValidationErrors。这个验证器使用 catchError *** 作符来处理任何潜在的错误。在这个例子中,验证器将 isAlterEgoTaken() 错误视为成功的验证,因为未能发出验证请求并不一定意味着这个第二人格无效。你也可以用不同的方式处理这种错误,比如返回 ValidationError 对象。

一段时间过后,这条可观察对象链完成,异步验证也就完成了。pending 标志位也设置为 false,该表单的有效性也已更新。

form.component.ts

contactForm: FormGroup = new FormGroup({
    username: new FormControl(
      "",
      [
        Validators.required,
        Validators.minLength(2)
      ],
      nameValidator.createValidator(this.nameService)
    )
  })
  
  constructor( private nameService:NameService) {
  }

name.service.ts

import { Injectable } from "@angular/core";
import {HttpClient} from "@angular/common/http";
import {delay,map,filter } from "rxjs/operators";
import { Observable } from "rxjs";

@Injectable()
export class NameService {
    constructor(private http: HttpClient) { }
    isAlterEgoTaken(alterEgo: string) {
        // 返回可观察对象Observable
        ..............
    }
}

自定义验证函数调用系统定义好的验证函数

首先创建validator.ts

src\app\validator.ts

import { AbstractControl, ValidatorFn, Validators } from "@angular/forms";

// 强转类型
export function isValidKey(
    key: string | number,
    object: object
): key is keyof typeof object {
    return key in object;
}

export function validator(data: object): ValidatorFn {
    return (control: AbstractControl): { [key: string]: any } | null => {
        for (const keyIndex in data) {
            if (isValidKey(keyIndex, data)) {
                if (Validators.required(control) && keyIndex == 'required' && data[keyIndex]) {
                    return {
                        required: data[keyIndex],
                        errMsg: "输入不能为空"
                    }
                }
                else if (Validators.email(control) && keyIndex == 'email'&& data[keyIndex]) {
                    return {
                        email: data[keyIndex],
                        errMsg: "输入必须为Email格式"
                    }
                }

                else if (/\s/.test(control.value) && keyIndex == 'cannotContainSpace'&& data[keyIndex]) {
                    return {
                        cannotContainSpace: data[keyIndex],
                        errMsg: "输入不能出现空格"
                    }
                }

                else if (/bob/i.test(control.value) && keyIndex == 'forbiddenName'&& data[keyIndex]) {
                    return {
                        forbiddenName: data[keyIndex],
                        errMsg: "输入不能出现bob"
                    }
                }

                else  if (keyIndex == 'minlength') {
                    if (Validators.minLength(data[keyIndex])) {
                        if (control.value.length < data[keyIndex]) {
                            return {
                                minlength: {
                                    requiredLength: data[keyIndex]
                                },
                                errMsg: "输入最短2个字符"
                            }
                        }
                    }

                }
            }
        }
        return null;

    }
}

Validators.required(control) && data.required调用内置验证器Validators中的required函数,且当外部约束required为true时进行验证,返回验证和验证结果。在new FormControl( )中设置Validator(参数)验证,在HTML中输出结果{{username?.errors?.['errMsg']}}不用判断错误类型,直接输出错误信息。

src\app\validator\validator.component.ts

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

@Component({
  selector: 'app-validator',
  templateUrl: './validator.component.html',
  styleUrls: ['./validator.component.css']
})

export class ValidatorComponent implements OnInit {

  vliData = {
    required: true,
    minlength: 2,
    forbiddenName: true,
    cannotContainSpace: true,
    email: false
  }
  
  contactForm: FormGroup = new FormGroup({
    username: new FormControl(
      "",
      validator(this.vliData)
    )
  })

  onSubmit() {
    console.log(this.contactForm.valid);
    console.log(this.username?.valueChanges);

  }

  get username() {
    return this.contactForm.get("username");

  }

  constructor() {
  }

  ngOnInit(): void {
  }

}

src\app\validator\validator.component.html

在模板驱动表单中验证

创建好需要的组件:在控制台输入 ng g component 组件名 可快速创建组件并自动注册

在终端中创建组件$ ng g component TemplateDrivenForms

在app.component.html文件中写入

src\app\template-driven-forms\template-driven-forms.component.html


src\app\template-driven-forms\template-driven-forms.component.ts

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

@Component({
  selector: 'app-template-driven-forms',
  templateUrl: './template-driven-forms.component.html',
  styleUrls: ['./template-driven-forms.component.css']
})
export class TemplateDrivenFormsComponent implements OnInit {

  constructor() {}

  ngOnInit(): void {

  }

  onSubmit(form:NgForm){
    console.log(form.valid);
  }
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/944875.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-18
下一篇 2022-05-18

发表评论

登录后才能评论

评论列表(0条)

保存