wake-up-neo.com

Angular 4: Die reaktive Formularsteuerung ist mit einem benutzerdefinierten Async-Prüfer im Status "Anstehend"

Ich baue eine Angular 4-App, die die BriteVerify-E-Mail-Validierung für Formularfelder in mehreren Komponenten erfordert. Ich versuche, diese Validierung als benutzerdefinierten asynchronen Validator zu implementieren, den ich mit reaktiven Formularen verwenden kann. Derzeit kann ich die API-Antwort erhalten, aber der Kontrollstatus bleibt im Status "Ausstehend". Ich bekomme keine Fehler und bin etwas verwirrt. Bitte sag mir, was ich falsch mache. Hier ist mein Code. 

Komponente

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

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

Bedienung

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

Vorlage (nur Formular)

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

5

Es gibt einegotcha!

Das heißt, Ihr Observable wird nie abgeschlossen ...

Dies geschieht, weil das Observable niemals abgeschlossen wird, so dass Angular nicht weiß, wann der Formularstatus geändert werden soll. Denken Sie also daran, dass Ihr beobachtbares Muss abgeschlossen sein muss.

Sie können dies auf verschiedene Arten erreichen. Sie können beispielsweise die first()-Methode aufrufen. Wenn Sie ein eigenes Observable erstellen, können Sie die komplette Methode auf dem Observer aufrufen.

Sie können also first() verwenden.

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

Ein leicht modifizierter Validator, d. H. Gibt immer einen Fehler zurück:STACKBLITZ

5
AJT_82

Ich habe es etwas anders gemacht und bin mit demselben Problem konfrontiert.

Hier ist mein Code und das Update für den Fall, dass jemand es brauchen würde:

  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;
  }
0
alexlz