Angular 4:反应式表单控件使用自定义异步验证器卡在挂起状态 [英] Angular 4: reactive form control is stuck in pending state with a custom async validator

查看:29
本文介绍了Angular 4:反应式表单控件使用自定义异步验证器卡在挂起状态的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在构建一个 Angular 4 应用程序,它需要对多个组件中的表单字段进行 BriteVerify 电子邮件验证.我正在尝试将此验证实现为可以与响应式表单一起使用的自定义异步验证器.目前,我可以得到 API 响应,但控制状态卡在挂起状态.我没有收到任何错误,所以我有点困惑.请告诉我我做错了什么.这是我的代码.

组件

import { 组件,OnInit } 来自'@angular/core';导入 { 表单生成器,表单组,表单控件,来自'@angular/forms'的验证器};从@angular/router"导入{路由器};从'../services/email-validation.service'导入{EmailValidationService};从'../utilities/custom-validators/custom-validators'导入{CustomValidators};@成分({templateUrl: './email-form.component.html',styleUrls: ['./email-form.component.sass']})导出类 EmailFormComponent 实现 OnInit {公共电子邮件表单:FormGroup;公共表单提交:布尔值;公共电子邮件发送:布尔值;构造函数(专用路由器:路由器,私人建造者:FormBuilder,私人服务:EmailValidationService) { }ngOnInit() {this.formSubmitted = false;this.emailForm = this.builder.group({电子邮件:[ '', [ Validators.required ], [ CustomValidators.briteVerifyValidator(this.service) ] ]});}获取电子邮件(){return this.emailForm.get('email');}//其余逻辑}

验证器类

import { AbstractControl } from '@angular/forms';从'../../services/email-validation.service'导入{EmailValidationService};从 'rxjs/Observable' 导入 { Observable };导入 'rxjs/add/observable/of';导入 'rxjs/add/operator/map';导入 'rxjs/add/operator/switchMap';导入 'rxjs/add/operator/debounceTime';导入 'rxjs/add/operator/distinctUntilChanged';导出类 CustomValidators {静态 briteVerifyValidator(服务:EmailValidationService){返回(控制:AbstractControl)=>{如果 (!control.valueChanges) {返回 Observable.of(null);} 别的 {返回 control.valueChanges.去抖动时间(1000).distinctUntilChanged().switchMap(value => service.validateEmail(value)).map(数据=> {返回 data.status === '无效' ?{无效:真}:空;});}}}}

服务

import { Injectable } from '@angular/core';导入 { HttpClient,HttpParams } 来自'@angular/common/http';接口电子邮件验证响应{地址:字符串,帐户:字符串,域:字符串,状态:字符串,连接:字符串,一次性:布尔值,role_address:布尔值,error_code?: 字符串,错误?:字符串,持续时间:数量}@Injectable()导出类 EmailValidationService {public emailValidationUrl = 'https://briteverifyendpoint.com';构造函数(私有 http: HttpClient) { }验证电子邮件(值){让参数 = 新的 HttpParams();params = params.append('地址', 值);return this.http.get(this.emailValidationUrl, {参数:参数});}}

模板(只是形式)

解决方案

有一个 问题

也就是说,你的 observable 永远不会完成......

<块引用>

发生这种情况是因为 observable 永远不会完成,所以 Angular 不知道何时更改表单状态.所以记住你的 observable 必须完成.

您可以通过多种方式完成此操作,例如,您可以调用 first() 方法,或者如果您正在创建自己的 observable,您可以在观察者上调用 complete 方法.

所以你可以使用first()

RXJS 6 更新:

briteVerifyValidator(service: Service) {返回(控制:AbstractControl)=>{如果 (!control.valueChanges) {返回(空);} 别的 {返回 control.valueChanges.pipe(去抖动时间(1000),distinctUntilChanged(),switchMap(value => service.getData(value)),地图(数据=> {返回 data.status === '无效' ?{无效:真}:空;})). 管道(第一个())}}}

稍微修改的验证器,即总是返回错误:STACKBLITZ

<小时>

旧:

.map(data => {返回 data.status === '无效' ?{无效:真}:空;}).第一的();

稍微修改的验证器,即总是返回错误:STACKBLITZ

I am building an Angular 4 app that requires the BriteVerify email validation on form fields in several components. I am trying to implement this validation as a custom async validator that I can use with reactive forms. Currently, I can get the API response, but the control status is stuck in pending state. I get no errors so I am a bit confused. Please tell me what I am doing wrong. Here is my code.

Component

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

import { EmailValidationService } from '../services/email-validation.service';

import { CustomValidators } from '../utilities/custom-validators/custom-validators';

@Component({
    templateUrl: './email-form.component.html',
    styleUrls: ['./email-form.component.sass']
})

export class EmailFormComponent implements OnInit {

    public emailForm: FormGroup;
    public formSubmitted: Boolean;
    public emailSent: Boolean;
    
    constructor(
        private router: Router,
        private builder: FormBuilder,
        private service: EmailValidationService
    ) { }

    ngOnInit() {

        this.formSubmitted = false;
        this.emailForm = this.builder.group({
            email: [ '', [ Validators.required ], [ CustomValidators.briteVerifyValidator(this.service) ] ]
        });
    }

    get email() {
        return this.emailForm.get('email');
    }

    // rest of logic
}

Validator class

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

import { EmailValidationService } from '../../services/email-validation.service';

import { Observable } from 'rxjs/Observable';

import 'rxjs/add/observable/of';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';

export class CustomValidators {

    static briteVerifyValidator(service: EmailValidationService) {
        return (control: AbstractControl) => {
            if (!control.valueChanges) {
                return Observable.of(null);
            } else {
                return control.valueChanges
                    .debounceTime(1000)
                    .distinctUntilChanged()
                    .switchMap(value => service.validateEmail(value))
                    .map(data => {
                        return data.status === 'invalid' ? { invalid: true } : null;
                    });
            }
        }
    }
}

Service

import { Injectable } from '@angular/core';
import { HttpClient,
         HttpParams } from '@angular/common/http';

interface EmailValidationResponse {
    address: string,
    account: string,
    domain: string,
    status: string,
    connected: string,
    disposable: boolean,
    role_address: boolean,
    error_code?: string,
    error?: string,
    duration: number
}

@Injectable()
export class EmailValidationService {

    public emailValidationUrl = 'https://briteverifyendpoint.com';

    constructor(
        private http: HttpClient
    ) { }

    validateEmail(value) {
        let params = new HttpParams();
        params = params.append('address', value);
        return this.http.get<EmailValidationResponse>(this.emailValidationUrl, {
            params: params
        });
    }
}

Template (just form)

<form class="email-form" [formGroup]="emailForm" (ngSubmit)="sendEmail()">
    <div class="row">
        <div class="col-md-12 col-sm-12 col-xs-12">
            <fieldset class="form-group required" [ngClass]="{ 'has-error': email.invalid && formSubmitted }">
                <div>{{ email.status }}</div>
                <label class="control-label" for="email">Email</label>
                <input class="form-control input-lg" name="email" id="email" formControlName="email">
                <ng-container *ngIf="email.invalid && formSubmitted">
                    <i class="fa fa-exclamation-triangle" aria-hidden="true"></i>&nbsp;Please enter valid email address.
                </ng-container>
            </fieldset>
            <button type="submit" class="btn btn-primary btn-lg btn-block">Send</button>
        </div>
    </div>
</form>

解决方案

There's a gotcha!

That is, your observable never completes...

This is happening because the observable never completes, so Angular does not know when to change the form status. So remember your observable must to complete.

You can accomplish this in many ways, for example, you can call the first() method, or if you are creating your own observable, you can call the complete method on the observer.

So you can use first()

UPDATE TO RXJS 6:

briteVerifyValidator(service: Service) {
  return (control: AbstractControl) => {
    if (!control.valueChanges) {
      return of(null);
    } else {
      return control.valueChanges.pipe(
        debounceTime(1000),
        distinctUntilChanged(),
        switchMap(value => service.getData(value)),
        map(data => {
          return data.status === 'invalid' ? { invalid: true } : null;
        })
      ).pipe(first())
    }
  }
}

A slightly modified validator, i.e always returns error: STACKBLITZ


OLD:

.map(data => {
   return data.status === 'invalid' ? { invalid: true } : null;
})
.first();

A slightly modified validator, i.e always returns error: STACKBLITZ

这篇关于Angular 4:反应式表单控件使用自定义异步验证器卡在挂起状态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
前端开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆