web-dev-qa-db-ja.com

Angular 4:リアクティブフォームコントロールは、カスタム非同期バリデーターで保留状態のままです

Angular 4アプリを構築しています。これは、いくつかのコンポーネントのフォームフィールドでBriteVerifyメール検証を必要とします。 、API応答は取得できますが、制御ステータスは保留状態のままです。エラーが発生しないため、少し混乱しています。何が間違っているのか教えてください。

成分

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

gotcha

つまり、あなたのオブザーバブルは決して完了しません...

これは、observableが完了しないために発生します。したがって、Angularはフォームのステータスをいつ変更するかわかりません。したがって、observableは完了する必要があります。

これはさまざまな方法で実現できます。たとえば、first()メソッドを呼び出したり、独自のオブザーバブルを作成している場合は、オブザーバーで完全なメソッドを呼び出すことができます。

したがって、first()を使用できます

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

わずかに変更されたバリデーター、つまり常にエラーを返します:[〜#〜] stackblitz [〜#〜]

12
AJT82

私はそれをわずかに異なってやっていて、同じ問題に直面しました。

ここに私のコードと誰かがそれを必要とする場合の修正があります:

  forbiddenNames(control: FormControl): Promise<any> | Observable<any> {
    const promise = new Promise<any>((resolve, reject) => {
      setTimeout(() => {
        if (control.value.toUpperCase() === 'TEST') {
          resolve({'nameIsForbidden': true});
        } else {

          return null;//HERE YOU SHOULD RETURN resolve(null) instead of just null
        }
      }, 1);
    });
    return promise;
  }
1
alexlz