当前位置: 首页 > 开发杂谈 >

[Angular] Day27. Validating form input

在前几篇中介绍了 Template-driven forms 和 reactive forms 的用法与概念,虽然建立的方式不同但在根本上都是可以建立一个表单的,而两种表单中都会需要验证使用者输入的资料的准确性与完整性来保持整体数据的质量,本章中将会介绍如何验证使用者输入的内容。

https://ithelp.ithome.com.tw/upload/images/20210826/20124767oGJRbnjfIU.jpg


Validating input in template-driven forms

首先先介绍 template-driven forms 中的 validator,要在 template-driven forms 中添加验证可以添加与原生 HTML 表单验证相同的验证 attribute,比如说可以在 <input> 中添加 required attribute 这样这个输入框就不能为空,又或是可以填入 max 代表填入的数字不能大於指定的数字等等,Angular 会使用 directive 将这些 attribute 和框架中的 validator functions 做匹配。

每次表单控制元件的值发生变化时,Angualr 都会运行验证并生成导致 INVALID 状态的错误列表或是生成 VALID 状态的 null

<div class="content">
  <input
    type="text"
    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">
    <div *ngIf="name.errors?.required" class="alert alert-danger">Name is required.</div>
    <div *ngIf="name.errors?.minlength" class="alert alert-danger">
      Name must be at least 4 characters long.
    </div>
    <div *ngIf="name.errors?.forbiddenName" class="alert alert-danger">Name cannot be Bob.</div>
  </div>
</div>

在一个 <input> 中加入 requiredminlengthappForbiddenName,代表这个输入框不能为空不能小於 4 个字且不能是 bob,可以利用 name ( template variable ) 的 errors 来判断是哪一个验证出错进而显示不同的提示,而使用 *ngIf 判断当状态为 INVALID表单已经被触碰过更改过时,才会显示提示画面。

  • 如果输入框为空

    https://ithelp.ithome.com.tw/upload/images/20210826/20124767admY7u506b.png

  • 如果输入的内容字数小於4个

    https://ithelp.ithome.com.tw/upload/images/20210826/20124767oNm1D5Kayu.png

    至於名字不能是 bob 由於这个是客制化验证的部分,留到後面再补上。


Validating input in reactive forms

在 Reactive forms 中的验证是通过直接在 component 中的 FormControl 添加验证,每当 FormControl 发生变化 Angular 就会调用这些函数,而 reactive forms 的验证器可以是同步也可以是非同步的

  • Sync validators:此用 FormControl 的实例并立即返回一组验证错误或是验证成功(null) 的同步函数,可以在实例化 FormControl 的时候将验证函数作为第二个参数传入。
  • Async validators:非同步验证函数接收一个 FormControl 并返回一个 promiseObservable,随後会发出一组验证错误或是验证成功(null),可以在实例化 FormControl 的时候将验证函数作为第三个参数传入。

设置同步与非同步验证器时需要注意,Angular 因为出於性能的问题,如果所有 Sync validators 都通过,Angular 就只会运行 Async validators。

Built-in validator functions

要设置 reactive forms 的验证器可以和 template-driven forms 在 template 中添加的验证器一样,比如使用 required 制定这个表单不能为空等等,以下列一个 validators 的表单

Name Description
min 输入的内容要大於等於 validator 设定的值
max 输入的内容要小於等於 validator 设定的值
required 输入的值不能为空
requiredTrue 输入的值必须为 true,常用在复选框中的必选栏位
email 输入的值需要通过电子邮件验证测试,常用正则表达式验证
minLength 要求输入的长度需要大於等於 validator 设置的值,他仅用於举有数字长度 property 的类型(字串或阵列)
maxLength 要求输入的长度需要小於等於 validator 设置的值,规则与 minLength 一样
pattern 输入的值需要匹配正则表达式的设定
nullValidator 不执行任何操作的 validator
compose 将多个 validator 合成一个函数,该函数返回提供 FormControl 的各个错误映射的联合
composeAsync 将多个 Async validator 合成一个函数,该函数返回提供 FormControl 的各个错误映射的联合
  1. 在 reactive.component.ts 中建立 FormControl

    import { Component, OnInit } from '@angular/core';
    import { FormControl, FormGroup, Validators } from '@angular/forms';
    
    @Component({
      selector: 'app-reactive',
      templateUrl: './reactive.component.html',
      styleUrls: ['./reactive.component.css'],
    })
    export class ReactiveComponent implements OnInit {
      hero = new FormGroup({                                                 // (1)
        name: new FormControl('', [Validators.required, Validators.minLength(4)]),
      });
      constructor() {}
    
      ngOnInit(): void {}
    
      errorDetect() {
        return this.name?.invalid && (this.name.touched || this.name.dirty); // (2)
      }
    
      getErrorType (): string {                                              // (3)
        if (this.name?.errors !== null) {
          if (this.name?.errors.required) {
            return 'required';
          } else if (this.name?.errors.minlength) {
            return 'minlength';
          }
        }
        return '';
      }
    
      get name() {                                                           // (4)
        return this.hero.get('name');
      }
    }
    
    • (1): 使用 FormGroup 和 FormControl 建立表单控制元件
    • (2): 新增一个 method 用於获得 name 的状态(是否是无效的内容、使否被处碰与是否被更改)
    • (3): 新增一个 method 用於获得 name 的错误类型
    • (4): 新增一个 get property 用於获得 name 的 FormControl 实例
  2. 在 reactive.component.html 中绑定 FormControl

    <form [formGroup]="hero">
      <div>
        <label for="name">Hero Name</label>
        <input type="text" id="name" class="form-control" formControlName="name" />
        <div *ngIf="errorDetect()" class="alert alert-danger">
          <ng-container [ngSwitch]="getErrorType()">
            <div *ngSwitchCase="'required'">Name is required.</div>
            <div *ngSwitchCase="'minlength'">
              Name must be at least 4 characters long.
            </div>
            <div *ngSwitchDefault></div>
          </ng-container>
        </div>
      </div>
    </form>
    

    在 tempalte 中绑定 component 中的 FormControl,这边我的写法会跟 Angular 官方文档的写法不一样,因为个人喜欢把显示的逻辑放在 component 中,所以无论是判断 name 是否 INVALID 或判断 name error 的型态都是绑定 component 中的 method。

https://ithelp.ithome.com.tw/upload/images/20210826/20124767ltGgbqbE4G.png

https://ithelp.ithome.com.tw/upload/images/20210826/20124767NDiGEUEh4U.png


Defining custom validators

除了上面介绍的预设 Validator function 之外还可以客制化自己的 Validator 用於验证各种表单内容,template-driven forms 和 reactive forms 都可以客制化自己的 Validator 但他们的方式不一样,首先先介绍 template-driven forms 的方式。

template-driven forms custom validators

在上面的例子中有使用一个 appForbiddenName 名称不能为 bob 的 validator,但上面目前不会对 bob 这个名字进行验证,是因为还没建立属於他的 validator,那麽就来创建这个客制化的验证器吧

  1. 使用 Angular CLI 建立一个 directive

    ng generate directive forbidden-name
    
  2. 在 forbidden-name.directive.ts 中加入 validator function

    import { Directive, Input } from '@angular/core';                     // (1)
    import {
      AbstractControl,
      NG_VALIDATORS,
      ValidationErrors,
      Validator,
      ValidatorFn,
    } from '@angular/forms';                                              // (2)
    
    @Directive({                                                          // (3)
      selector: '[appForbiddenName]',
      providers: [{provide: NG_VALIDATORS, useExisting: ForbiddenNameDirective, nulti: true}]
    })
    export class ForbiddenNameDirective implements Validator {            // (4)
      @Input('appForbiddenName') forbiddenName = '';                      // (5)
      constructor() {}
    
      forbiddenNameValidator(nameRe: RegExp): ValidatorFn {               // (6)
        return (control: AbstractControl): ValidationErrors | null => {
          const forbidden = nameRe.test(control.value);
          return forbidden ? { forbiddenName: { value: control.value } } : null;
        };
      }
    
      validate(control: AbstractControl): ValidationErrors | null {       // (7)
        return this.forbiddenName
          ? this.forbiddenNameValidator(new RegExp(this.forbiddenName, 'i'))(
              control
            )
          : null;
      }
    }
    
    • (1): 从 @angular/core 中引入 Input
    • (2): 从 @angular/forms 中引入
      • AbstractControl 用於为传入 validate function 的 input 定义型态
      • NG_VALIDATORS 用於注册与 AbstractControl 一起使用的 sync validators InjectionToken
      • ValidationErrors 用於定义失败的验证检查返回错误的映射
      • Validator 是执行 sync validator 的 class 的 Interface
      • ValidatorFn 用於接收 FormControl 并 sync 的返回验证错误的映射或返回 null
    • (3): NG_VALIDATORS 使用 useExistingForbiddenNameDirective 为模板创建一个实例
    • (4): 使用 implements 来实践抽象的 interface ( Validator )
    • (5): 使用 @Input() 装饰 forbiddenName property 为输入,并取别名为 appForbiddenName
    • (6): 新增一个 method 用於接收字串後判断字串是否符合验证规定,返回验证错误的映照或 null
    • (7): 新增一个一个 method 用於验证输入的字串是否符合验证规定

https://ithelp.ithome.com.tw/upload/images/20210826/20124767lyWNYBJbAf.png

reactive forms custom validators

介绍完 template-driven forms 的客制化验证器後,接着介绍 reactive forms 的客制化验证,他不像 template-driven forms 一样需要建立一个 directive,只需要在 component 中的 FormControl 加入客制化验证函数即可。

  1. 先在 reactive.component.ts 中新增一个客制化验证函数

    forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
      return (control: AbstractControl): ValidationErrors | null => {
        const forbidden = nameRe.test(control.value);
        return forbidden ? { forbiddenName: { value: control.value } } : null;
      };
    }
    
  2. 在 reactive.component.ts 中 FormGroup 的 name (FormControl) 的 Validator 中添加客制化验证函数

hero = new FormGroup({
  name: new FormControl('', [
    Validators.required,
    Validators.minLength(4),
    this.forbiddenNameValidator(/hank/i),
  ]),
  alterEgo: new FormControl(''),
  power: new FormControl('', Validators.required),
});

https://ithelp.ithome.com.tw/upload/images/20210826/20124767wlh2Pe5yek.png


结论

本章介绍了如何在 template-driven forms 和 reactive forms 中添加预设的 validators function,也介绍了如何建立客制化的 validators function 然後加入到两种不同的 forms 中,表单验证在开发表单时非常重要,可以确保资料的确定性。

明天将会介绍 Form 的另一种使用方法,虽然再开发大型表单时可以使用 FormGroup 将类似的 FormControl 分组管理,但真的很大型的表单时还是会显得非常杂乱难以维护,这时就需要使用 Control Value Accessor 这个功能,可以把它想像成 component 中的父子层,在上层的 Component 中定义大概的 Form Model,将细节放在子层的 component 中,这样将整个表单内容以 Component 分割,可以更好的维护与测试,详细该怎麽使用就明天再详细讲解吧,那麽明天见


Reference


相关文章:

  • 对零售业实用的5个社交媒体广告宣传方式
  • 香港公司利得税详解
  • Day4-Go Go Go!第一只 golang!
  • 最简单的 Google Maps 嵌入方式 | 专案实作
  • 亚马逊视频展示的类型有哪些?
  • 速卖通卖家数据化选品的方法和技巧
  • lazada关于Chat的常见问题解答
  • Day 25 constructors、this、static
  • Facebook对公账户验证
  • [13th][Day30] 结语
  • 小白签到: 纪录python学习day 1
  • 旺季亚马逊卖家如何预防账号关联问题
  • 轻松救回被删语音备忘录
  • DAY 10 Big Data 5Vs – Velocity(多样性) DynamoDB
  • #26 No-code 之旅 — 实作 Dark Mode 和加入 Google Fonts ft. Chakra UI
  • Pinterest的商业指南(信息图):为什么以及如何在在Pinterest上推广你的业务
  • Hostinger主机如何添加一个新的域名(网站),如何更改主机的主域名(Main Domain)
  • 最好的国外VPS推荐:国外好用便宜性价比高的VPS有哪些
  • 401错误怎么处理?教你如何解决网站401错误页面
  • VPS优惠:搬瓦工香港VPS补货/G口/KVM/月付9.9美元
  • Facebook和instagram推广营销教程
  • 一周要闻:谷歌母公司、Facebook、亚马逊等几大互联网公司一季度财报
  • DGCHOST稳定吗?现在还可以购买DGCHOST的服务器VPS吗
  • WooCommerce 带动顾客购物的十种方法
  • 搬瓦工VPS优惠码/ 促销码 、最新BandwagonHost官网促销
  • 如何使用Hostinger的邮箱服务,Hostinger免费企业邮箱设置教程
  • 一键脚本:SmokePing一键安装/管理脚本
  • 财富自由怎么实现?如何做到财富自由
  • 专业提供东南亚-越南线上支付通道
  • 新的 Web.com 电子商务平台帮助中小企业接触数百万潜在买家