javascript - Angular 4 : reactive form control is stuck in pending state with a custom async validator

标签 javascript angular angular-reactive-forms angular-validation

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

组件

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
}

验证器类

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;
                    });
            }
        }
    }
}

服务

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
        });
    }
}

模板(只是表格)

<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>

最佳答案

有一个 gotcha !

也就是说,您的可观察对象永远不会完成...

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.

所以你可以使用first()

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())
    }
  }
}

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


旧:

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

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

关于javascript - Angular 4 : reactive form control is stuck in pending state with a custom async validator,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48655324/

相关文章:

javascript - 如何将来自服务器的数据设置为这种动态形式?

angular - 在哪里初始化 Angular 4 组件中的变量 - ExpressionChangedAfterItHasBeenCheckedError

javascript - Tablesorter addParser 用于日期,如 "aa, dd.mm.yyyy"

testing - angular2 测试,使用 setBaseTestProviders() 时 ViewUtils 出错

typescript - 在 Angular CLI 中使用第三方库类型

javascript - 发送自定义表单控件验证器 FormArray

angular - 在 Angular 5 中将数据从服务获取到 react 形式的最佳方式

angular - react 形式改变 Angular 2

javascript - 结合 React、Redux 和 React-Router 时,正确的做法是什么?

javascript - 类点击不会触发