AngularfireでFirebase Authenticationを利用するときにログイン中を検出する方法について

# はじめに

[AngularFire](https://github.com/angular/angularfire2)を使うと[Firebase](https://firebase.google.com/docs/web/setup?hl=ja)と[Angular](https://angular.io/)を簡単に連携することができます。
この記事では、AngularFireを使って[Authentication](https://firebase.google.com/docs/auth/web/start?hl=ja)を利用する場合にログイン中を検出する方法について解説します。

# この記事のゴール

- AngularFireのサンプルをベースにログイン処理を実装
- 最近のブラウザではポップアップが抑止されるため、ログインページにリダイレクト[signInWithRedirect](https://firebase.google.com/docs/auth/web/google-signin?hl=ja)を利用して実装
- ログイン中にローディング画面を表示

# AngularFireの[サンプル](https://github.com/angular/angularfire2/blob/master/docs/auth/getting-started.md)の解説

```ts
import { Component } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { AngularFireAuth } from 'angularfire2/auth';
import * as firebase from 'firebase/app';

@Component({
  selector: 'app-root',
  template: `
    <div *ngIf="afAuth.authState | async; let user; else showLogin">
      <h1>Hello {{ user.displayName }}!</h1>
      <button (click)="logout()">Logout</button>
    </div>
    <ng-template #showLogin>
      <p>Please login.</p>
      <button (click)="login()">Login with Google</button>
    </ng-template>
  `, 
})
export class AppComponent {
  constructor(public afAuth: AngularFireAuth) {
  }
  login() {
    this.afAuth.auth.signInWithPopup(new firebase.auth.GoogleAuthProvider());
  }
  logout() {
    this.afAuth.auth.signOut();
  }
}
```
- `template`
  -  `authState`が存在するするときにユーザ名とログアウトボタンを表示
  -  `authState`が存在しないときにログインボタンを表示
- `constructor()`
  - `AngularFireAuth`をDI
- `login()`
  - `signInWithPopup()`を実行してログイン
- logout()
  - `signOut()` を実行してログアウト

# ログイン処理をリダイレクトに変更

```ts
  login() {
    // this.afAuth.auth.signInWithPopup(new firebase.auth.GoogleAuthProvider());
    this.afAuth.auth.signInWithRedirect(new firebase.auth.GoogleAuthProvider());
  }
```
- 単純にメソッド名を変更

## ログイン処理変更による画面遷移の影響
  - 変更前  
      1. [メイン画面]ログインボタン表示 => クリック
      2. [ポップアップ画面]ログインページがポップアップ
      3. [ポップアップ画面]ログイン処理が完了するポップアップが閉じる
      4. [メイン画面]ログイン後の画面が表示    
  - 変更後
      1. [メイン画面]ログインボタン表示 => クリック
      2. [メイン画面]ログインページにリダイレクト
      3. [メイン画面]ログインボタン表示 => **ローディング**画面に変更したい
      4. [メイン画面]ログイン後の画面が表示  

# ローディング画面の追加

```html
<h1 *ngIf="isRunning; else showCompleted">Loading ...</h1>
<ng-template #showCompleted >
  <div *ngIf="afAuth.authState | async; let user; else showLogin">
    <h1>Hello {{ user.displayName }}!</h1>
    <button (click)="logout()">Logout</button>
  </div>
  <ng-template #showLogin>
    <p>Please login.</p>
    <button (click)="login()">Login with Google</button>
  </ng-template>
<ng-template>

```
```ts
export class AppComponent {
  isRunning = true;
  // :
}
```

- `isRunning`のステータスを追加して、ローディング画面を追加
- ログイン処理が完了した後に、`isRunning`が`false`になればOK!

# 処理中のステータスの更新処理の追加

```ts
export class AppComponent {
  isRunning = true;
  constructor(public afAuth: AngularFireAuth) {
    this.afAuth.authState.subscribe(() => {
      this.isRunning = false;
    })
  }
  login() {
    this.isRunning = true;
    this.afAuth.auth.signInWithRedirect(new firebase.auth.GoogleAuthProvider());
  }
  logout() {
    this.isRunning = true;
    this.afAuth.auth.signOut();
  }
```
- constructor()
  - `authState`を取得できたら処理完了と判断
- logout() / logout()
  - 処理中に遷移

これでログイン中にローディング画面を表示することが可能になります。

# まとめ

完成後のソース

```ts
import { Component } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { AngularFireAuth } from 'angularfire2/auth';
import * as firebase from 'firebase/app';

@Component({
  selector: 'app-root',
  template: `
<h1 *ngIf="isRunning; else showCompleted">Loading ...</h1>
<ng-template #showCompleted >
  <div *ngIf="afAuth.authState | async; let user; else showLogin">
    <h1>Hello {{ user.displayName }}!</h1>
    <button (click)="logout()">Logout</button>
  </div>
  <ng-template #showLogin>
    <p>Please login.</p>
    <button (click)="login()">Login with Google</button>
  </ng-template>
<ng-template>
  `
})
export class AppComponent {
  isRunning = true;
  constructor(public afAuth: AngularFireAuth) {
    this.afAuth.authState.subscribe(() => {
      this.isRunning = false;
    })
  }
  login() {
    this.isRunning = true;
    this.afAuth.auth.signInWithRedirect(new firebase.auth.GoogleAuthProvider());
  }
  logout() {
    this.isRunning = true;
    this.afAuth.auth.signOut();
  }
}
```
完成後の画面遷移

1. [メイン画面]ログインボタン表示 => クリック
2. [メイン画面]ローディング画面を表示
3. [メイン画面]ログインページにリダイレクト
4. [メイン画面]ローディング画面を表示
5. [メイン画面]ログイン後の画面が表示