中安拓也のブログ

プログラミングについて書くブログ

【Angular】HttpInterceptorを使ってスピナーを表示する

f:id:l08084:20190922162235p:plain
スピナーが表示されている様子

はじめに

Angular4.3で追加されたHttpClientのInterceptorという機能を使うことで、HttpClientを使ったWebAPI呼び出しのタイミングで実行したい共通処理を定義することができます。

今回はこのInterceptor機能を使うことでAPIの呼び出し中にスピナーを表示する処理を実装します。

バージョン情報

CSSフレームワークとして、Angular Materialを使用しています。

  • Angular v7.2.0
  • firebase: v6.3.4
  • Angular Material v7.3.7

開発

スピナーコンポーネントとスピナーサービス、インターセプターを作成することでスピナーの表示処理を実装します。

HTTPリクエストを検知したインターセプターから受け取った、true/falseの値をスピナーサービス経由でスピナーコンポーネントに渡してスピナーの表示/非表示を切り替えるといった流れになります。

AppModuleの設定

まず、AppModuleのimports[]にAngular MaterialのProgress spinner と AngularのHttpClientModuleを追加します。

import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { HttpClientModule } from '@angular/common/http';
//...省略

@NgModule({
  //...省略
  imports: [
    HttpClientModule,
    MatProgressSpinnerModule,
  ],
  //...省略
})
export class AppModule {}

スピナーコンポーネントの作成

スピナーのコンポーネントクラスを作成していきます。

スピナーコンポーネントでは、インターセプターから渡させたbooleanの値をもとにスピナーの表示と非表示を実施します。

color, mode, valueはスピナーの色などを設定するAngular Material Spinnerのプロパティになります。

isLoadingはスピナーの表示・非表示を制御する変数で詳細は後述します。

スピナーのコンポーネントクラス

スピナーコンポーネントのHTMLファイルです。

isLoadingSubject型なので、コンポーネント側でsubscribeするかasyncパイプを使用する必要があります。

スピナーコンポーネントクラスのテンプレートファイル

スピナーコンポーネントのレイアウトを設定するSCSSファイルです。

スピナーコンポーネントクラスのSCSSファイル

スピナーサービスの作成

続いてスピナーサービスを作成します。

showhideメソッドを経由して、isLoadingtrue/falseを渡すことで、スピナーの表示・非表示を切り替えています。

スピナーのサービスクラス

インターセプターの作成

インターセプターSpinnerInterceptorを作成します。

HTTP リクエストが始まったタイミングでSpinnerServiceshowメソッドを呼び出してスピナーを表示し、リクエストが完了したタイミングでhideメソッドを呼び出してスピナーを消しています。

スピナーの表示・非表示を制御するインターセプター

AppModuleの設定

AppModuleのproviders: []に作成したスピナーサービスとインターセプターを追加します。

  providers: [
    SpinnerService,
    {
      provide: HTTP_INTERCEPTORS,
      useClass: SpinnerInterceptor,
      multi: true
    }
  ],

最後にapp.component.htmlにスピナーコンポーネントを追加して、スピナーの実装は完了です。

<app-spinner></app-spinner>
<router-outlet></router-outlet>

動作確認

実装が正しくできているか確認します。

HttpClientによるHTTPリクエスト

ここまでの実装が正しくできていれば、HttpClientを介してHTTPリクエストを実施するとスピナーが表示され、リクエストが完了するとスピナーが消えます。

下のコードでは、yesno.wtf APIをHttpClientで呼び出しているため、リクエストの間、スピナーが表示されます。

// ...省略
import { HttpClient } from '@angular/common/http';

export class LoginComponent implements OnInit {

  constructor(
    private http: HttpClient
  ) {}

  public ngOnInit() {
    this.http
      .get(`https://yesno.wtf/api`)
      .subscribe(response => console.log(response));
  }

HttpClientを使わない場合

Firebaseへのアクセスなどの、Angular HttpClientを使用しないHTTPアクセスの場合は、インターセプターが反応しません。そのため、直接SpinnerServiceのメソッドを叩いてスピナーを表示・非表示する必要があります。

下記のFirebase認証を実施しているコードでは、処理が開始したタイミングでshowメソッドでスピナーを表示し、処理が完了(finally)したタイミングでhideメソッドを呼び出すことでスピナーを消しています。

  public onSubmit() {
    // メールアドレスとパスワードをFirebase Authenticationに渡す
    // スピナーを表示する
    this.spinnerService.show();
    this.afAuth.auth
      .signInWithEmailAndPassword(
        this.emailControl.value,
        this.passwordControl.value
      )
      // ログインに成功したらホーム画面に遷移する
      .then(user => {
        this.router.navigate(['/home']);
      })
      // ログインに失敗したらエラーメッセージをログ出力
      .catch(error => {
        console.log(error);
        this.apiErrorMessage = error ? error.message : undefined;
      })
      // 処理が完了したら、スピナーを非表示にする
      .finally(() => this.spinnerService.hide());
  }

参考サイト

Display a loader on every HTTP request using Interceptor in Angular 7 - First Class JS