0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Ionic + Firebaseで記録するカレンダーアプリを作る その5 ユーザー作成と認証

Last updated at Posted at 2019-11-12

#完成イメージ

#はじめに
環境は以下の通り
・ionic4
・Nativeとの橋渡しにはcapacitorを使用
・カレンダーについてはionic2-calendarというライブラリーを使用

前回はdayページからイベントを追加、更新、削除ができるようになった。
:point_right:前回の記事はこちら

・フォームの入力チェックについては以下を参考に
ionic フォームで変なこと書いてないか、バリデーションを実装

・ログイン時のFirebase側でのエラーを表示する方法については以下を参考に
Firebase ionic サインアップのエラータイプを判別して、アラート表示を変える
#概要
このアプリは各アカウントがイベントを持つ事で、ようやく自分以外の人でも使えるようになる。そうでないと全ユーザーが共通のカレンダーを見る事になるからね・・・

Ionic4 + Firebaseでのユーザー認証はこちらで詳しく解説されている。
私自身もこの記事を参考に作った。

今回はとりあえずユーザーを作成して、そのユーザーでログインできるようになる所までやってみる。

ログインしているユーザーごとに、表示するカレンダーを変えるのは次回という事で。

#FirebaseコンソールでAuthenticationを設定
メールアドレスとパスワードでの認証を有効にする。

スクリーンショット 2019-11-12 19.21.25.jpg

#必要なモジュールをapp.module.tsで登録
AngularFireAuthModuleをimportして@NgModuleに追加

app.module.ts
import { NgModule, LOCALE_ID } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';

import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx';

import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';

import { HttpClientModule } from '@angular/common/http';

//カレンダー関係
import { NgCalendarModule  } from 'ionic2-calendar';
import { registerLocaleData } from '@angular/common';
import localeJa from '@angular/common/locales/ja';

//Firebaseを利用するためのモジュール
import { AngularFireModule } from '@angular/fire';
import { AngularFirestoreModule } from '@angular/fire/firestore';
import { AngularFireAuthModule } from '@angular/fire/auth';

//Firebase設定情報ファイルをインポート
import { environment } from '../environments/environment';

registerLocaleData(localeJa);


@NgModule({
  declarations: [AppComponent],
  entryComponents: [],
  imports: [
    BrowserModule,
    IonicModule.forRoot(),
    AppRoutingModule,
    NgCalendarModule,
    HttpClientModule,
    AngularFireModule.initializeApp(environment.firebase),
    AngularFirestoreModule,
    AngularFireAuthModule
  ],
  providers: [
    StatusBar,
    SplashScreen,
    NativeStorage,
    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
    { provide: LOCALE_ID, useValue: 'ja-JP' }
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

#authページを作る
本当は素直にloginページと呼びたい所だが、認証関係の処理をhomeページでもやったようにserviceにまとめたい。

ログイン以外にも認証(Authentication)したい時は出てくるので、ここではauthページとして作成したいと思う。

ionic g page auth

##auth.service.ts
ionic g service auth/auth

auth.service.ts
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { AngularFireAuth } from '@angular/fire/auth';
import { Observable } from 'rxjs';
import { AngularFirestore, AngularFirestoreDocument } from '@angular/fire/firestore';
import { switchMap } from 'rxjs/operators';
import { LoadingController, AlertController, ToastController } from '@ionic/angular';

export interface User {
  uid: string;
  email: string;
  displayName?: string;
  myCustomData?: string;
  photoURL?: string;
}

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  user$: Observable<User[]>;

  constructor(
    private router: Router,
    private afs: AngularFirestore,
    private afAuth: AngularFireAuth,
    public loadingCtrl: LoadingController,
    private alertCtrl: AlertController,
    private toastCtrl: ToastController
  ) {
    this.user$ = this.afAuth.authState.pipe(
      switchMap(user => {
        if (user) {
          this.updateUserData(user);
          return this.afs.doc<User[]>(`users/${user.uid}`).valueChanges();
        } else {
          this.router.navigateByUrl('/auth');
        }
      })
    );
  }

  async login(email: string, password: string) {
    await this.afAuth.auth
      .signInWithEmailAndPassword(email, password)
      .then(async user => {
        this.router.navigateByUrl('/home');
        const toast = await this.toastCtrl.create({
          message: `${this.afAuth.auth.currentUser.displayName}さん、こんにちは!`,
          duration: 3000
        });
        await toast.present();
      })
      .catch(error => {
        console.log(error);
        const code = error.code;
        const header = 'ログイン失敗';
        let message = 'エラーが発生しました。もう一度お試しください。。。';
        if (code === 'auth/user-not-found') {
          message =
            'ユーザーが見つかりません。メールアドレスを確認して下さい。。。';
        } else if (code === 'auth/wrong-password') {
          message =
            'そのメールアドレスは存在していますが、パスワードが違います。。。';
        }
        this.showAlert(header, message);
        this.router.navigateByUrl('/auth');
      });
  }

  private showAlert(header: string, message: string) {
    this.alertCtrl
      .create({
        header: header,
        message: message,
        buttons: ['OK']
      })
      .then(alertEl => alertEl.present());
  }

  async logout() {
    this.afAuth.auth.signOut();
  }

  async presentLoading(loading) {
    return await loading.present();
  }

  updateUserData(user) {
    // ログイン時にユーザーデータをfirestoreに設定
    // usersコレクションの下に入る
    const userRef: AngularFirestoreDocument<User> = this.afs.doc(
      `users/${user.uid}`
    );
    const data = {
      uid: user.uid,
      email: user.email,
      displayName: user.displayName,
      photoURL: user.photoURL
    };
    return userRef.set(data, { merge: true });
  }

  getUser(): Observable<User[]> {
    return this.user$;
  }
}

##auth.page.html
実質上のログインページ

auth.page.html
<ion-header>
  <ion-toolbar color="primary">
    <ion-title>ログイン</ion-title>
  </ion-toolbar>
</ion-header>

<ion-content padding>
  <ion-grid>
    <ion-row>
      <ion-col size="12" size-sm="8" offset-sm="2">
        <ion-item class="loginHeader" lines="none" text-center>
        </ion-item>
        <form #f="ngForm" class="loginForm">
          <ion-item class="formItem">
            <ion-label position="floating">
              <ion-icon name="mail"></ion-icon>
              メールアドレス
            </ion-label>
            <ion-input required email [(ngModel)]="login.email" type="email" name="login.email" #emailCtrl="ngModel">
            </ion-input>
          </ion-item>
          <ion-item *ngIf="!emailCtrl.valid && emailCtrl.touched" lines="none">
            <ion-label color="danger">メールアドレスを入力してね!</ion-label>
          </ion-item>
        
          <ion-item class="formItem">
            <ion-label position="floating">
              <ion-icon name="key" padding-right></ion-icon>パスワード
            </ion-label>
            <ion-input required [(ngModel)]="login.password" type="password" name="login.password" #passwordCtrl="ngModel"
              minlength="6">
            </ion-input>
          </ion-item>
          <ion-item *ngIf="!passwordCtrl.valid && passwordCtrl.touched" lines="none">
            <ion-label color="danger">パスワードは6文字以上</ion-label>
          </ion-item>
        
          <ion-button class="submitBtn" [disabled]="!f.form.valid" (click)="userLogin()" seize="large" expand="block">
            ログイン
          </ion-button>
        </form>
      </ion-col>
    </ion-row>
  
  <ion-row>
    <ion-col size="12" size-sm="8" offset-sm="2">
      <ion-button class="subLink" (click)="gotoSignup()" fill="clear">
        アカウント登録はこちらから
      </ion-button>
    </ion-col>
  </ion-row>
</ion-grid>
</ion-content>

##auth.page.ts

auth.page.ts
import { AuthService } from './auth.service';
import { Router } from '@angular/router';
import { Component, OnInit } from '@angular/core';
import { ToastController } from '@ionic/angular';
import { AngularFireAuth } from '@angular/fire/auth';
import { LoadingController } from '@ionic/angular';

@Component({
  selector: 'app-auth',
  templateUrl: './auth.page.html',
  styleUrls: ['./auth.page.scss']
})
export class AuthPage implements OnInit {
  login: {
    email: string;
    password: string;
  } = {
    email: '',
    password: ''
  };

  constructor(
    private router: Router,
    private authService: AuthService,
    private toastCtrl: ToastController,
    private afAuth: AngularFireAuth,
    private loadingCtrl: LoadingController,
  ) {}

  ngOnInit() {}

  async userLogin() {
    const loading = await this.loadingCtrl.create({
      message: 'ログイン中です'
    });
    this.presentLoading(loading);
    this.authService.login(this.login.email, this.login.password)
    .then(() => {
      loading.dismiss();
    });
  }

  gotoSignup() {
    this.router.navigateByUrl('/signup');
  }

  async presentLoading(loading) {
    return await loading.present();
  }
}

#signupページを作る

ionic g page signup

##signup.page.html

signup.page.html
<ion-header>
  <ion-toolbar color="primary">
    <ion-title>アカウント登録</ion-title>
  </ion-toolbar>
</ion-header>

<ion-content padding>
  <ion-grid>
    <ion-row>
      <ion-col size="12" size-sm="8" offset-sm="2">
        <form #f="ngForm" class="signupForm">
          <ion-item class="formItem">
            <ion-label position="floating">
              <ion-icon name="mail"></ion-icon>メールアドレス
            </ion-label>
            <ion-input required email [(ngModel)]="signup.email" type="email" name="signup.email" #emailCtrl="ngModel">
            </ion-input>
          </ion-item>
          <ion-item *ngIf="!emailCtrl.valid && emailCtrl.touched" lines="none">
            <ion-label color="danger">メールアドレスを入力してね!</ion-label>
          </ion-item>
          <ion-item class="formItem">
            <ion-label position="floating">
              <ion-icon name="key"></ion-icon>パスワード
            </ion-label>
            <ion-input required [(ngModel)]="signup.password" type="password" name="signup.password" minlength="6"
              #passwordCtrl="ngModel">
            </ion-input>
          </ion-item>
          <ion-item *ngIf="!passwordCtrl.valid && passwordCtrl.touched" lines="none">
            <ion-label color="danger">パスワードは6文字以上</ion-label>
          </ion-item>
          <ion-item class="formItem">
            <ion-label position="floating">
              <ion-icon name="person"></ion-icon>名前
            </ion-label>
            <ion-input required [(ngModel)]="signup.name" type="text" name="signup.name"></ion-input>
          </ion-item>
        
          <ion-button class="submitBtn" [disabled]="!f.form.valid" (click)="signUp()" size="large" expand="block">
            アカウント登録
          </ion-button>
        </form>
      </ion-col>
    </ion-row>

  <ion-row>
    <ion-col size="12" size-sm="8" offset-sm="2">
      <p>アカウント登録を完了すると、
        <a href="">プライバシーポリシー</a>
        に同意した事になります。</p>
    </ion-col>
  </ion-row>

  <ion-row>
    <ion-col size="12" size-sm="8" offset-sm="2">
      <ion-button class="subLink" (click)="goLogin()" fill="clear">
        ログインはこちら
      </ion-button>
    </ion-col>
  </ion-row>
  </ion-grid>
</ion-content>

##signup.page.ts

signup.page.ts
import { Router } from '@angular/router';
import { AuthService } from './../auth/auth.service';
import { Component, OnInit } from '@angular/core';
import { AngularFireAuth } from '@angular/fire/auth';
import { ToastController, AlertController } from '@ionic/angular';

@Component({
  selector: 'app-signup',
  templateUrl: './signup.page.html',
  styleUrls: ['./signup.page.scss']
})
export class SignupPage implements OnInit {
  signup: {
    email: string;
    password: string;
    name: string;
  } = {
      email: '',
      password: '',
      name: ''
    };

  constructor(
    private router: Router,
    private afAuth: AngularFireAuth,
    private toastCtrl: ToastController,
    public authService: AuthService,
    private alertCtrl: AlertController
  ) { }

  ngOnInit() { }

  signUp() {
    this.afAuth.auth
      .createUserWithEmailAndPassword(this.signup.email, this.signup.password)
      .then(created => {
        const newUser = created.user;
        newUser.updateProfile({
          displayName: this.signup.name,
          photoURL: ''
        })
          .then(async () => {
            console.log('signup ' + this.signup.name);
            const toast = await this.toastCtrl.create({
              message: `${created.user.displayName}さんを登録しました`,
              duration: 3000
            });
            this.authService.updateUserData(created.user);
            this.router.navigateByUrl('/home');
            await toast.present();
          });
      })
      .catch(error => {
        console.log(error);
        const code = error.code;
        let message = 'エラーが発生しました。もう一度お試しください。。。';
        if (code === 'auth/email-already-in-use') {
          message = 'このメールアドレスは既に登録されています。。。';
        }
        this.showAlert(message);
      });
  }

  goLogin() {
    this.router.navigateByUrl('/auth');
  }

  private showAlert(message: string) {
    this.alertCtrl
      .create({
        header: 'ユーザー登録に失敗',
        message: message,
        buttons: ['OK']
      })
      .then(alertEl => alertEl.present());
  }
}

urlの最後を/signupにしてアクセスしてみましょう。
アカウント登録とログインがうまくいってるなら、Firebaseコンソールでもユーザーが登録されていることが確認できる。

#最後に
続き:point_down:
その6 ログアウト処理とサイドバー

ログアウトがまだ一切できていないので、次回やりたい。

各ストアで配信されてます。
興味があれば使ってみてください。:relieved:

Apple Store

Google Play

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?