title: Angular 4: reactive form control is stuck in pending state with a custom async validator
body: <p>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. </p>
  
  <h2>Component</h2>
  
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>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
  }</code></pre>
  </div>
  </div>
  </p>
  
  <h2>Validator class</h2>
  
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>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) =&gt; {
              if (!control.valueChanges) {
                  return Observable.of(null);
              } else {
                  return control.valueChanges
                      .debounceTime(1000)
                      .distinctUntilChanged()
                      .switchMap(value =&gt; service.validateEmail(value))
                      .map(data =&gt; {
                          return data.status === 'invalid' ? { invalid: true } : null;
                      });
              }
          }
      }
  }</code></pre>
  </div>
  </div>
  </p>
  
  <h2>Service</h2>
  
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>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&lt;EmailValidationResponse&gt;(this.emailValidationUrl, {
              params: params
          });
      }
  }</code></pre>
  </div>
  </div>
  </p>
  
  <h2>Template (just form)</h2>
  
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;form class="email-form" [formGroup]="emailForm" (ngSubmit)="sendEmail()"&gt;
      &lt;div class="row"&gt;
          &lt;div class="col-md-12 col-sm-12 col-xs-12"&gt;
              &lt;fieldset class="form-group required" [ngClass]="{ 'has-error': email.invalid &amp;&amp; formSubmitted }"&gt;
                  &lt;div&gt;{{ email.status }}&lt;/div&gt;
                  &lt;label class="control-label" for="email"&gt;Email&lt;/label&gt;
                  &lt;input class="form-control input-lg" name="email" id="email" formControlName="email"&gt;
                  &lt;ng-container *ngIf="email.invalid &amp;&amp; formSubmitted"&gt;
                      &lt;i class="fa fa-exclamation-triangle" aria-hidden="true"&gt;&lt;/i&gt;&amp;nbsp;Please enter valid email address.
                  &lt;/ng-container&gt;
              &lt;/fieldset&gt;
              &lt;button type="submit" class="btn btn-primary btn-lg btn-block"&gt;Send&lt;/button&gt;
          &lt;/div&gt;
      &lt;/div&gt;
  &lt;/form&gt;</code></pre>
  </div>
  </div>
  </p>