Angular + @angular/fire でサインイン/サインアップ/ログアウトできるページを作る

AngularとFirebaseでサインイン/サインアップ/ログアウトができるページを作成します。Angularは1系以外まったくいじったことがない初心者です。

今回の完成品は

- https://github.com/hbsnow-sandbox/angular-firebase-auth/tree/1.0.0

ここにあります。

AngularでFirebaseをいい感じに使うライブラリにはいくつかあるようだけど、@angular/fire が公式でメンテナンスされているもののようなので、こちらを使うことにします（前はangularfire2みたいな名前だったらしい）。

```terminal
ng new angular-firebase-auth
npm i -S @angular/fire
touch src/environments/environment.local.ts
```

## Firebase

- https://firebase.google.com/

上記にいってアカウントを作成して、コンソールに移動した後、適当にプロジェクトを作成して、アプリを追加からウェブのプラットフォームを追加する。

メニューからAuthenticationを選択してログイン方法のタブを選択。今回はログイン方法にメールアドレスとGoogleを採用しました。

![Screenshot from 2019-12-12 22-55-58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17669/0f8d9e97-b196-4412-0311-9e92f0b00eb1.png)

次に必要なapiKeyなどの情報を `touch src/environments/environment.local.ts` にコピペ。

```javascript

export const environment = {
  production: false,
  firebase: {
    apiKey: "",
    ...
  }
};
```

こんなかんじ。`environment.local.ts` は `.gitignore` に含めてgitから除外しておいたけど、実際に使う場合にどうするのが一般的なのかはちょっと調べてみてもよくわからなかった。

あとは `angular.json` に作ったファイルの設定がlocalで実行時に反映されるように

```json
"configurations": {
  "local": {
    "fileReplacements": [
      {
        "replace": "src/environments/environment.ts",
        "with": "src/environments/environment.local.ts"
      }
    ]
  },
  ...
}
```

を追加してFirebaseの設定は終わり。`ng serve --configuration=local` で実行するとこの設定が反映されるようになる。

## Angular

環境変数の設定までは終わっているけど、いくつか設定がたりてないので下記をみて不足している記述を追加する。主に `app.module.ts` になると思う。

- [1. Installation and Setup](https://github.com/angular/angularfire/blob/master/docs/install-and-setup.md)

次に認証用のコードを書いていく

```terminal
ng g s shared/authentication
```

ここでは下記のドキュメント通りではなく認証用のサービスを作る

- [5. Getting started with Firebase Authentication](https://github.com/angular/angularfire/blob/master/docs/auth/getting-started.md)


[FirebaseのAPI](https://firebase.google.com/docs/reference/js/firebase.auth.Auth)を参考にしながら、コードを次のようにした

```javascript
import { Injectable } from '@angular/core';
import { AngularFireAuth } from "@angular/fire/auth";
import { Observable } from 'rxjs';
import { auth } from 'firebase/app';

@Injectable({
  providedIn: 'root'
})
export class AuthenticationService {
  user: Observable<firebase.User>;

  constructor(private angularFireAuth: AngularFireAuth) {
    this.user = angularFireAuth.authState;
  }

  SignUp(email: string, password: string) {
    this.angularFireAuth
      .auth
      .createUserWithEmailAndPassword(email, password)
      .then((response) => {
        console.log(response);
      })
      .catch((err) => {
        console.error(err);
      });
  }

  SignIn(email: string, password: string) {
    this.angularFireAuth
      .auth
      .signInWithEmailAndPassword(email, password)
      .then((response) => {
        console.log(response);
      })
      .catch((err) => {
        console.error(err);
      });
  }

  SignInGoogle() {
    this.angularFireAuth
      .auth
      .signInWithPopup(new auth.GoogleAuthProvider())
      .then((response) => {
        console.log(response);
      })
      .catch((err) => {
        console.error(err);
      });
  }

  SignOut() {
    this.angularFireAuth
      .auth
      .signOut();
  }
}
```

あとになって `SignInGoogle()` は引数に `firebase.auth.AuthProvider` の型を渡せるようにあげるようにしたほうがよかったのかも、と思ったけど他がログインに何を使うか調べてないので実際にいい方法なのかどうかはちょっとわからない。

とりあえずエラーはコンソールにはくだけにしてます、とりあえず動けばいい。

次にHTMLとTS。

```html
<div *ngIf="authenticationService.user | async as user; else showLogin">
  <p>{{ user?.email }} でログイン中</p>
  <button (click)="signOut()" *ngIf="user">ログアウト</button>
</div>

<ng-template #showLogin>
  <p><label>email: <input type="text" [(ngModel)]="email" placeholder="email"></label></p>
  <p><label>password: <input type="password" [(ngModel)]="password" placeholder="password"></label></p>

  <p><button (click)="signUp()">サインアップ</button></p>
  <p><button (click)="signIn()">サインイン</button></p>
  <p><button (click)="signInGoogle()">Googleでサインイン</button></p>
</ng-template>
```

```typescript
import { Component } from '@angular/core';
import { AuthenticationService } from './shared/authentication.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'angular-firebase-auth';
  email: string;
  password: string;

  constructor(public authenticationService: AuthenticationService) {}

  signUp() {
    this.authenticationService.SignUp(this.email, this.password);
  }

  signIn() {
    this.authenticationService.SignIn(this.email, this.password);
  }

  signInGoogle() {
    this.authenticationService.SignInGoogle();
  }

  signOut() {
    this.authenticationService.SignOut();
  }
}
```

このあたりはFirebaseうんぬんよりも圧倒的Angular力の低さによって、とにかくAngularに苦戦した。async pipeとか `as` は良い学びにはなったと思う。

asについては [[Angular 4.0] 新しいngIfの使い方](https://blog.lacolaco.net/post/ng4-feature-ngif/) がわかりやすかった。