Angularで「Expected validator to return Promise or Observable」エラーが発生した時の解決方法

2024-04-02

JavaScript、Angular、Angular5 における「Expected validator to return Promise or Observable」エラー

Angularフォームバリデーションにおいて、asyncValidators で非同期処理を行う場合、Promise または Observable を返す必要があります。これが満たされない場合、「Expected validator to return Promise or Observable」というエラーが発生します。

原因

このエラーは以下の理由で発生します。

  • asyncValidators で返された値が Promise または Observable ではない
  • asyncValidators 関数内でエラーが発生している

解決方法

このエラーを解決するには、以下のいずれかの方法を試します。

asyncValidators は、Promise または Observable を返す関数です。これらのいずれかを返していない場合は、適切な型に変換する必要があります。

例:Promise を返す場合

asyncValidators: {
  userName: (control: FormControl) => Promise<ValidationErrors | null> {
    return new Promise((resolve) => {
      // 非同期処理
      setTimeout(() => {
        if (control.value === 'invalid') {
          resolve({
            userName: 'ユーザー名が不正です'
          });
        } else {
          resolve(null);
        }
      }, 1000);
    });
  }
}

例:Observable を返す場合

asyncValidators: {
  userName: (control: FormControl) => Observable<ValidationErrors | null> {
    return new Observable((observer) => {
      // 非同期処理
      setTimeout(() => {
        if (control.value === 'invalid') {
          observer.next({
            userName: 'ユーザー名が不正です'
          });
        } else {
          observer.next(null);
        }
      }, 1000);
    });
  }
}

asyncValidators 関数内でエラーが発生している場合、エラー内容を確認して修正する必要があります。

エラー内容の確認方法

ブラウザの開発者ツールを使用して、コンソールログを確認します。コンソールログには、エラーメッセージとエラーが発生した箇所が表示されます。

エラーの修正例

asyncValidators: {
  userName: (control: FormControl) => Promise<ValidationErrors | null> {
    return new Promise((resolve) => {
      // 非同期処理
      setTimeout(() => {
        // ここでエラーが発生
        if (control.value === undefined) {
          resolve({
            userName: 'ユーザー名が未入力です'
          });
        } else {
          resolve(null);
        }
      }, 1000);
    });
  }
}

上記の例では、control.valueundefined の場合にエラーが発生しています。このエラーを修正するには、undefined の場合も適切な処理を行うようにコードを変更する必要があります。

上記以外にも、このエラーを解決するための方法はいくつか考えられます。詳細については、Angular ドキュメントや関連資料を参照してください。




app.component.ts

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

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {

  form: FormGroup;

  constructor() { }

  ngOnInit() {
    this.form = new FormGroup({
      userName: new FormControl('', [
        Validators.required,
        this.userNameAsyncValidator()
      ])
    });
  }

  userNameAsyncValidator(): AsyncValidatorFn {
    return (control: FormControl) => new Promise((resolve) => {
      // 非同期処理
      setTimeout(() => {
        if (control.value === 'invalid') {
          resolve({
            userName: 'ユーザー名が不正です'
          });
        } else {
          resolve(null);
        }
      }, 1000);
    });
  }
}
<form [formGroup]="form">
  <input type="text" formControlName="userName" />
  <button type="submit">送信</button>
</form>

このコードを実行すると、ユーザー名が "invalid" の場合、「ユーザー名が不正です」というエラーメッセージが表示されます。

import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators, AsyncValidatorFn } from '@angular/forms';
import { Observable } from 'rxjs';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {

  form: FormGroup;

  constructor() { }

  ngOnInit() {
    this.form = new FormGroup({
      userName: new FormControl('', [
        Validators.required,
        this.userNameAsyncValidator()
      ])
    });
  }

  userNameAsyncValidator(): AsyncValidatorFn {
    return (control: FormControl) => new Observable((observer) => {
      // 非同期処理
      setTimeout(() => {
        if (control.value === 'invalid') {
          observer.next({
            userName: 'ユーザー名が不正です'
          });
        } else {
          observer.next(null);
        }
      }, 1000);
    });
  }
}

上記のコードは、Promise の代わりに Observable を使用しています。




「Expected validator to return Promise or Observable」エラーを解決する他の方法

asyncValidators 関数内で async キーワードを使用すると、自動的に Promise が返されます。

例:

asyncValidators: {
  userName: (control: FormControl) => async () => {
    // 非同期処理
    const response = await fetch('https://api.example.com/users/' + control.value);
    const data = await response.json();

    if (data.valid === false) {
      return {
        userName: 'ユーザー名が不正です'
      };
    }

    return null;
  }
}

from 関数は、Observable を生成する関数です。asyncValidators 関数内で from を使用すると、Observable を返すことができます。

asyncValidators: {
  userName: (control: FormControl) => Observable<ValidationErrors | null> {
    return from(fetch('https://api.example.com/users/' + control.value)).pipe(
      map((response) => response.json()),
      map((data) => {
        if (data.valid === false) {
          return {
            userName: 'ユーザー名が不正です'
          };
        }

        return null;
      })
    );
  }
}

サードパーティライブラリを使用する

ngx-formly などのサードパーティライブラリを使用すると、asyncValidators をより簡単に記述することができます。

import { FormlyFieldConfig } from 'ngx-formly';

const fields: FormlyFieldConfig[] = [
  {
    key: 'userName',
    type: 'input',
    validators: {
      asyncValidators: {
        userName: (control: FormControl) => {
          return this.userService.validateUserName(control.value);
        }
      }
    }
  }
];

javascript angular angular5


Cache-Control ヘッダーを使用して $.ajax リクエストのキャッシュを制御する

iOS 6 の Safari は、デフォルトで $.ajax の GET リクエスト結果をキャッシュします。POST リクエストは、デフォルトではキャッシュされません。キャッシュの動作は、Cache-Control ヘッダーや Expires ヘッダーによって制御できます。...


rootオプションを使用して「TypeError: path must be absolute or specify root to res.sendFile」エラーを解決する

Node. jsでres. sendFile()メソッドを使用する際に、「TypeError: path must be absolute or specify root to res. sendFile」というエラーが発生することがあります。これは、ファイルパスの指定方法に問題があることを示しており、適切な修正が必要です。...


Angular 2 で推奨されるフォルダ構造

以下のフォルダ構成は、Angular 2プロジェクトの出発点としてよく使用されます。各フォルダの説明app/:アプリケーションのメインフォルダ。app. component. ts:アプリケーションのルートコンポーネント。app. module...


Angular 2 エラー: 'ngModel' にバインドできない: 'input' の既知のプロパティではない

Angular 2 テンプレートで ngModel ディレクティブを使って input 要素にバインドしようとすると、以下のエラーが発生する:原因:このエラーは、input 要素に ngModel ディレクティブを正しく適用していないことが原因です。...


依存関係管理を容易にする:Angularコンポーネントにおけるインジェクターインスタンスの保存

依存関係の管理:コンポーネントが複数のサービスや他の依存関係に依存している場合、それらを明示的にコンポーネントのコンストラクタに注入することは煩雑になる可能性があります。インジェクターインスタンスを保存することで、コンポーネントが必要とする依存関係を簡単にアクセスおよび管理することができます。...