Angular学习笔记51:响应式表单的异步校验器

Angular学习笔记50:响应式表单动态修改表单校验器之后,有时会遇到对于某一个控件,它要有整个系统的唯一性校验,但是又不想通过在用户填写完表单以后,再告知用户,填写的某一个控件内容重复,需要重新填写,这样的用户体验并不是最佳的,应在用户填写完具有唯一性校验的控件以后,就发起校验,然而发起校验是要通过远侧服务器来发起校验的,整个过程是异步完成的,所以需要一个异步校验器。

如何写一个异步的校验器呢?

言简意赅的讲,异步的校验器,其实也是一个服务,是实现了AsyncValidator接口的服务。

创建一个 async-demo 的组件

执行命令

ng g c async-demo

执行结果:

wujiayudeMBP:demo-test wjy$ ng g c async-demo
CREATE src/app/async-demo/async-demo.component.less (0 bytes)
CREATE src/app/async-demo/async-demo.component.html (29 bytes)
CREATE src/app/async-demo/async-demo.component.spec.ts (650 bytes)
CREATE src/app/async-demo/async-demo.component.ts (285 bytes)
UPDATE src/app/app.module.ts (2285 bytes)

在 async-demo 中添加一个表单和一个基本Input的控件

修改ansyc-demo的模版文件:

<nz-content>
  <nz-divider nzText="异步校验器"></nz-divider>
  <form nz-form [formGroup]="validateForm">
    <nz-form-item>
      <nz-form-label nzSpan="3" nz-col nzRequired>demo姓名</nz-form-label>
      <nz-form-control nzSpan="6" nz-col>
        <label>
          <input nz-input formControlName="demoName" name="demoName" type="text">
        </label>
      </nz-form-control>
    </nz-form-item>
  </form>
</nz-content>

修改ansyc-demo的类文件:

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

@Component({
  selector: 'app-async-demo',
  templateUrl: './async-demo.component.html',
  styleUrls: ['./async-demo.component.less']
})
export class AsyncDemoComponent implements OnInit {
  public validateForm: FormGroup;

  constructor(private fb: FormBuilder) {
    this.validateForm = this.fb.group({
      demoName: [null, [Validators.required]]
    });
  }

  ngOnInit() {
  }

}

创建一个服务,并实现AsyncValidator接口

  1. 创建服务
    执行命令:
ng g s demoNameAsyncValidator

执行结果:

wujiayudeMBP:public wjy$ ng g s demoNameAsyncValidator
CREATE src/app/public/demo-name-async-validator.service.spec.ts (416 bytes)
CREATE src/app/public/demo-name-async-validator.service.ts (151 bytes)
  1. 实现AsyncValidator接口
import {Injectable} from '@angular/core';
import {AbstractControl, AsyncValidator, ValidationErrors} from '@angular/forms';
import {Observable, of} from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class DemoNameAsyncValidatorService implements AsyncValidator {

  constructor() {
  }

  validate(control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {
    
  }
}

完善这个异步校验器

修改validate方法

import {Injectable} from '@angular/core';
import {AbstractControl, AsyncValidator, ValidationErrors} from '@angular/forms';
import {Observable, of} from 'rxjs';
import {HttpClient} from '@angular/common/http';
import {catchError, map} from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class DemoNameAsyncValidatorService implements AsyncValidator {

  constructor(private httpService: HttpClient) {
  }

  validate(control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {
    const validatorUrl = `/nest/demo?param=${control.value}`;
    return control.dirty ? this.httpService.get(validatorUrl).pipe(map(isTrue => {
      return (isTrue && control.dirty ? {demoNameValidator: true} : null);
    }), catchError(() => null)) : of(null);
  }
}

这里的接口是使用Nest.js写的,比较简单,接口中的逻辑是:只要接受到的参数是"demo" 就返回true,代表重复了,否则返回false,代表不重复。

Nest.js代码如下:

import { Controller, Get, Param, Query } from '@nestjs/common';

@Controller('demo')
export class DemoController {
  @Get()
  public handleValidatorName(@Query('param') param): boolean {
    return param === 'demo';
  }
}

将这个异步校验加到控件实例中:

修改组件async-demo的类文件

import {Component, OnInit} from '@angular/core';
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
import {DemoNameAsyncValidatorService} from '../public/demo-name-async-validator.service';

@Component({
  selector: 'app-async-demo',
  templateUrl: './async-demo.component.html',
  styleUrls: ['./async-demo.component.less']
})
export class AsyncDemoComponent implements OnInit {
  public validateForm: FormGroup;

  constructor(private fb: FormBuilder, private demoNameAsync: DemoNameAsyncValidatorService) {
    this.validateForm = this.fb.group({
      demoName: [null, [Validators.required], [this.demoNameAsync.validate.bind(this.demoNameAsync)]]
    });
  }

  ngOnInit() {
  }

}

保存运行;

在这里插入图片描述

然后就会发现,校验器会对每次输入的数据进行校验,这样的校验代价非常巨大,所以,在这里要修改检测方式。

减小校验代价

方式:修改检测方式由原有的 updateOn 属性从 change(默认值)改成 submit 或 blur;在这里修改为blur。

修改组件的类文件

import {Component, OnInit} from '@angular/core';
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
import {DemoNameAsyncValidatorService} from '../public/demo-name-async-validator.service';

@Component({
  selector: 'app-async-demo',
  templateUrl: './async-demo.component.html',
  styleUrls: ['./async-demo.component.less']
})
export class AsyncDemoComponent implements OnInit {
  public validateForm: FormGroup;

  constructor(private fb: FormBuilder, private demoNameAsync: DemoNameAsyncValidatorService) {
    this.validateForm = this.fb.group({
      demoName: [null, {
        validators: [Validators.required],
        asyncValidators: [this.demoNameAsync.validate.bind(this.demoNameAsync)],
        updateOn: 'blur'
      }]
    });
  }

  ngOnInit() {
  }

}

保存运行,会发现只有在失去焦点以后,才会发送异步校验的请求。

在这里插入图片描述

添加校验中的提示和校验失败的提示

在异步校验器发送请求的过程中,此控件的状态是pending的,所以,添加校验中的提示如下:

 <nz-form-explain *ngIf="validateForm.get('demoName').dirty&& validateForm.get('demoName').pending">
正在校验中...
</nz-form-explain>

在异步校验器发送请求的完毕,根据远端服务器返回的结果判断,所以,添加校验失败提示如下:

<nz-form-explain *ngIf="validateForm.get('demoName').dirty
 && validateForm.get('demoName').hasError('demoNameValidator')">
填写内容重复,请重新填写...
 </nz-form-explain>

所以修改后的组件类文件为:

<nz-content>
  <nz-divider nzText="异步校验器"></nz-divider>
  <form nz-form [formGroup]="validateForm">
    <nz-form-item>
      <nz-form-label nzSpan="3" nz-col nzRequired>demo姓名</nz-form-label>
      <nz-form-control nzSpan="6" nz-col>
        <label>
          <input nz-input formControlName="demoName" name="demoName" type="text">
        </label>
        <nz-form-explain *ngIf="validateForm.get('demoName').dirty
                            && validateForm.get('demoName').pending">
          正在校验中...
        </nz-form-explain>
        <nz-form-explain *ngIf="validateForm.get('demoName').dirty
                            && validateForm.get('demoName').hasError('demoNameValidator')">
          填写内容重复,请重新填写...
        </nz-form-explain>
      </nz-form-control>
    </nz-form-item>
  </form>
</nz-content>

模版文件:

import {Component, OnInit} from '@angular/core';
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
import {DemoNameAsyncValidatorService} from '../public/demo-name-async-validator.service';

@Component({
  selector: 'app-async-demo',
  templateUrl: './async-demo.component.html',
  styleUrls: ['./async-demo.component.less']
})
export class AsyncDemoComponent implements OnInit {
  public validateForm: FormGroup;

  constructor(private fb: FormBuilder, private demoNameAsync: DemoNameAsyncValidatorService) {
    this.validateForm = this.fb.group({
      demoName: [null, {
        validators: [Validators.required],
        asyncValidators: [this.demoNameAsync.validate.bind(this.demoNameAsync)],
        updateOn: 'blur'
      }]
    });
  }

  ngOnInit() {
  }

}

异步校验器:

import {Injectable} from '@angular/core';
import {AbstractControl, AsyncValidator, ValidationErrors} from '@angular/forms';
import {Observable, of} from 'rxjs';
import {HttpClient} from '@angular/common/http';
import {catchError, map} from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class DemoNameAsyncValidatorService implements AsyncValidator {

  constructor(private httpService: HttpClient) {
  }

  validate(control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {
    const validatorUrl = `/Nest/demo?param=${control.value}`;
    return control.dirty ? this.httpService.get(validatorUrl).pipe(map(isTrue => {
      return (isTrue && control.dirty ? {demoNameValidator: true} : null);
    }), catchError(() => null)) : of(null);
  }
}

以上代码可以参考W先生的GitHub

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值