如何在Angular中使用自定义表单验证
介绍
验证器用于确保表单中的值满足某些要求。 它们可用于 Angular 应用程序中的 模板驱动表单 或 反应式表单。
有几个内置的验证器,例如 required、email、pattern 和 minLength。 还可以开发自定义验证器来解决内置验证器无法处理的功能。
例如,电话号码验证器将包含一个输入字段,除非该值长为十位,否则不会被视为有效。
这是一个电话号码输入字段的屏幕截图,该字段提供了一个九位数的无效号码:
这是一个电话号码输入字段的屏幕截图,该字段提供了一个十位数长的有效号码:
在本教程中,您将为 Angular 应用程序中的电话号码输入字段构建一个自定义验证器。
先决条件
要完成本教程,您需要:
- Node.js 安装在本地,您可以按照【X57X】如何安装Node.js 并创建本地开发环境【X126X】进行。
- 熟悉 设置 Angular 项目 。
本教程已使用 Node v15.2.1、npm v6.14.8、@angular/core v11.0.0 和 @angular/forms v11.0.0 进行了验证。
设置项目
就本教程而言,您将从使用 @angular/cli 生成的默认 Angular 项目构建。
npx @angular/cli new angular-custom-validation-example --style=css --routing=false --skip-tests
注意:或者,您可以全局安装 @angular/cli。
这将配置一个新的 Angular 项目,其样式设置为“CSS”(与“Sass”、“Less”或“Stylus”相对)、无路由和跳过测试。
导航到新创建的项目目录:
cd angular-custom-validation-example
此时,您将拥有一个新的 Angular 项目。
在模板驱动的表单中使用验证器
Directives 用于在模板驱动的表单中进行验证。 对于此示例,您将使用 @angular/cli 创建一个 phone-number-validator 指令。
首先,打开终端并使用作为开发依赖项安装的 @angular/cli 包生成新指令:
./node_modules/@angular/cli/bin/ng generate directive phone-number-validator
这将创建 phone-number-validator.directive.ts 和 phone-number-validator.directive.spec.ts。 它还将 PhoneNumberValidatorDirective 添加到 app.module.ts。
接下来,在代码编辑器中打开 phone-number-validator.directive.ts。 添加 Validator、AbstractControl 和 NG_VALIDATORS:
src/app/phone-number-validator.directive.ts
import { Directive } from '@angular/core';
import { AbstractControl, Validator, NG_VALIDATORS } from '@angular/forms';
@Directive({
selector: '[appPhoneNumberValidator]',
providers: [{
provide: NG_VALIDATORS,
useExisting: PhoneNumberValidatorDirective,
multi: true
}]
})
export class PhoneNumberValidatorDirective implements Validator {
validate(control: AbstractControl) : {[key: string]: any} | null {
if (control.value && control.value.length != 10) {
return { 'phoneNumberInvalid': true };
}
return null;
}
}
这段代码创建了一个实现 @angular/forms 的 Validator 的指令。 它将需要以下实现方法:validate(control: AbstractControl): : {[key: string]: any} | null。 此验证器将返回一个对象 - { 'phoneNumberInvalid': true } - 如果值不等于长度不等于十个字符的条件。 否则,如果值满足条件,则返回null。
接下来,打开终端并使用作为开发依赖项安装的 @angular/cli 包生成新指令:
./node_modules/@angular/cli/bin/ng generate component template-driven-form-example --flat
此命令将创建 template-driven-form-example.component.ts 和 template-driven-form-example.component.html 文件。 它还将 TemplateDrivenFormExampleComponent 添加到 app.module.ts。
接下来,在代码编辑器中打开 template-driven-form-example.component.ts 并添加初始值为空字符串的 phone:
src/app/template-driven-form-example.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-template-driven-form-example',
templateUrl: './template-driven-form-example.component.html',
styleUrls: ['./template-driven-form-example.component.css']
})
export class TemplateDrivenFormExampleComponent {
phone = '';
}
Angular 在 FormControl / NgModel 的 errors 属性中添加验证函数的返回值。 如果 FormControl / NgModel 的 errors 属性不为空,则表单无效。 如果 errors 属性为空,则表单有效。
要在模板驱动的表单中使用该指令,请打开 template-driven-form-example.component.html 并添加以下代码:
src/app/template-driven-form-example.component.html
<div class="form-group">
<label>Phone
<input
type="text"
class="form-control"
name="phone"
[(ngModel)]="phone"
#phoneNgModel="ngModel"
appPhoneNumberValidator
[class.is-invalid]="(phoneNgModel.touched || phoneNgModel.dirty) && phoneNgModel.errors?.phoneNumberInvalid"
>
</label>
<span
class="invalid-feedback"
*ngIf="(phoneNgModel.touched || phoneNgModel.dirty) && phoneNgModel.errors?.phoneNumberInvalid"
>
Phone number must be 10 digits
</span>
</div>
此代码创建一个 <input> 元素和 <span> 并带有错误消息。 <input> 元素使用指令的 ngModel 和 appPhoneNumberValidator 选择器。
如果 <input> 已经是 touched 或 dirty 并且验证不通过,则会发生两种情况。 首先,类 is-invalid 将应用于 <input>。 其次,将显示带有错误消息的<span>。
注意: 这里的一些类 - form-group、form-control、invalid-feedback 和 is-valid - 是 Bootstrap 框架的一部分。 这些不是完成本教程所必需的,但可以为表单提供视觉美感。
然后,在代码编辑器中打开 app.module.ts 并添加 FormModule:
src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { PhoneNumberValidatorDirective } from './phone-number-validator.directive';
import { TemplateDrivenFormExampleComponent } from './template-driven-form-example.component';
@NgModule({
declarations: [
AppComponent
PhoneNumberValidatorDirective,
TemplateDrivenFormExampleComponent
],
imports: [
BrowserModule,
FormsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
最后,打开 app.component.html 并将内容替换为您的 TemplateDrivenFormExample:
src/app/app.component.html
<app-template-driven-form-example></app-template-driven-form-example>
您可以运行 npm start 命令并在 Web 浏览器中与您的输入进行交互。 如果您在电话字段中输入少于或多于 10 个字符,则会显示错误消息。
此时,您有一个使用模板驱动表单中的指令的自定义验证器。
在响应式表单中使用验证器
响应式表单不是使用指令,而是使用函数进行验证。
首先,打开终端并使用作为开发依赖项安装的 @angular/cli 包生成新指令:
./node_modules/@angular/cli/bin/ng generate component reactive-form-example --flat
此命令将创建 reactive-form-example.component.ts 和 reactive-form-example.component.html 文件。 它还将 ReactiveFormExampleComponent 添加到 app.module.ts。
接下来,在代码编辑器中打开 reactive-form-example.component.ts 并添加 FormBuilder 和 AbstractControl:
src/app/reactive-form-example.component.ts
import { Component, OnInit } from "@angular/core";
import { AbstractControl, FormBuilder, FormGroup } from '@angular/forms';
@Component({
selector: 'app-reactive-form-example',
templateUrl: './reactive-form-example.component.html',
styleUrls: ['./reactive-form-example.component.css']
})
export class ReactiveFormExampleComponent implements OnInit {
myForm: FormGroup;
constructor(private fb: FormBuilder) {}
ngOnInit(): void {
this.myForm = this.fb.group({
phone: ['', [ValidatePhone]]
});
}
saveForm(form: FormGroup) {
console.log('Valid?', form.valid); // true or false
console.log('Phone Number', form.value.phone);
}
}
function ValidatePhone(control: AbstractControl): {[key: string]: any} | null {
if (control.value && control.value.length != 10) {
return { 'phoneNumberInvalid': true };
}
return null;
}
此代码创建一个 ValidatePhone 函数并将其添加到 FormControl 的验证器数组中。
在代码编辑器中打开 reactive-form-example.component.html 并创建以下表单:
src/app/reactive-form-example.component.html
<form
class="needs-validation"
novalidate
[formGroup]="myForm"
(ngSubmit)="saveForm(myForm)"
>
<div class="row">
<div class="form-group col-sm-4">
<label>
Phone
<input
type="text"
class="form-control"
formControlName="phone"
[class.is-invalid]="(myForm.get('phone').touched || myForm.get('phone').dirty) && myForm.get('phone').invalid"
>
</label>
<span
class="invalid-feedback"
*ngIf="(myForm.get('phone').touched || myForm.get('phone').dirty) && myForm.get('phone').invalid"
>
Phone number must be 10 digits
</span>
</div>
</div>
</form>
与模板驱动的表单不同,此表单具有 form 并使用 [formGroup]、(ngSubmit)、formControlName 和 get。
然后,在代码编辑器中打开 app.module.ts 并添加 ReactiveFormsModule:
src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { PhoneNumberValidatorDirective } from './phone-number-validator.directive';
import { ReactiveFormExampleComponent } from './reactive-form-example.component';
import { TemplateDrivenFormExampleComponent } from './template-driven-form-example.component';
@NgModule({
declarations: [
AppComponent,
PhoneNumberValidatorDirective,
ReactiveFormExampleComponent,
TemplateDrivenFormExampleComponent
],
imports: [
BrowserModule,
FormsModule,
ReactiveFormsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
最后,打开 app.component.html 并将内容替换为您的 ReactiveFormExample:
src/app/app.component.html
<app-reactive-form-example></app-reactive-form-example>
您可以运行 npm start 命令并在 Web 浏览器中与您的输入进行交互。 如果您在电话字段中输入少于或多于 10 个字符,则会显示错误消息。
此时,您有一个使用反应形式的函数的自定义验证器。
结论
在本文中,向您介绍了在 Angular 应用程序中为模板驱动表单和反应式表单添加自定义验证。
自定义验证允许您确保用户提供的值符合您的期望。
要更深入地了解这篇文章中的概念,请访问 this post on Providers 和 了解 AbstractControl。