中安拓也のブログ

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

【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

【Python】楽天カードの利用明細をダウンロードする

前回の記事

www.l08084.com

前回に引き続き、クレジットカードの利用明細のダウンロードをしていきます。

前回は、UIテスト自動化ツールのSeleniumuとChrome Driverをインストールして、ドコモのクレジットカードであるdカードの利用明細をダウンロードするところまでやりました。

バージョン情報

PCはmacOS Mojave(10.14.6)

  • selenium: 3.141.0
  • chrome: 76.0
  • ChromeDriver: 76.0
  • Python 3.7.3

楽天カードの利用明細をダウンロードする

f:id:l08084:20190915175346p:plain
ブラウザを自動操縦して利用明細をダウンロードしている様子
今回は、楽天カードの利用明細を引き続きPythonを使ってダウンロードしていきます。

今回は利用明細のダウンロードにrequestsモジュールを使用するため、ターミナルを開いて下記のコマンドを実行する必要があります。

$ pip install requests

続いて、Chromeを自動操縦して楽天カードの利用明細をダウンロードするPythonのコードを書きます。

楽天カードの利用明細をダウンロードするコード

前回同様、楽天カードサイトのユーザIDとパスワードはrakuten_credentials.pyファイルに別途定義しています。

苦労した点

f:id:l08084:20190915174543p:plain
利用明細CSVのダウンロードリンク

<a href="XX" class="stmt-c-btn-dl stmt-csv-btn">

利用明細のCSVリンクを取得する時に、CSSのクラス名からアンカータグのDOMを取得しようとしましたが、複数のclassが設定されていたため、どのメソッドで取得できるかがわかりませんでした。

python - Selenium Compound class names not permitted - Stack Overflow

この問題は上記のstackoverflowのサイトで解決しました。CSSのclassが二つ以上ある要素をSeleniumのfind_element_by_class_nameで取得しようとすると失敗しますが、代わりにfind_element_by_css_selectorを使えば取得できます。

tag = driver.find_element_by_css_selector('.stmt-c-btn-dl.stmt-csv-btn')

参考サイト

ゼロからはじめるPython(49) Pythonでブラウザ自動操縦してカード明細を自動でダウンロードしよう(その1) | マイナビニュース

ゼロからはじめるPython(50) Pythonでブラウザ自動操縦してカード明細を自動でダウンロードしよう(その2) | マイナビニュース

【Git】既存のリポジトリから別のリポジトリを作成する

f:id:l08084:20190908203621p:plain 一瞬forkかなと思ったけどforkだとcommit履歴も持ってきちゃうし...

やりたいこと

  • リポジトリAをベースにリポジトリBを作成したい
  • commit履歴は引き継ぎたくない
  • 開発ブランチも引き継ぎたくない

バージョン情報

使用しているPCはmacです

  • macOS Mojave
  • git version 2.20.1

やってみる

Github上のl08084/taikinリポジトリを複製してl08084/conceptリポジトリを作成します。

なお、複製先のリモートリポジトリ(l08084/concept)は事前にGitHubなどで作成しておく必要があります。

# 複製したいリポジトリをクローンする
$ git clone git@github.com:l08084/taikin.git

# 複製したいリポジトリのプロジェクト配下に移動する
$ cd taikin

# gitの設定情報を全て削除(commit履歴、開発ブランチetc...)
$ rm -rf .git

# 複製先のリモートリポジトリへのpush
$ git init
$ git add .
$ git commit -m "Initial commit"
$ git remote add origin git@github.com:l08084/concept.git
$ git push -u origin maste

これでリポジトリの複製は完了です。

ポイントは、rm -rf .gitコマンドでgitの設定を全て削除しているところになります。
この手順を抜かしてしまうとgit remote add originコマンドを実行した時にリモートリポジトリの参照名であるoriginが衝突してfatal: remote origin already exists.エラーが発生してしまいます。

【Ionic】WebサイトからiOSアプリを開くリンク(「アプリで開く」ボタン)を実装する

f:id:l08084:20190907170235p:plain

仕事で「iPhone/iPad端末にアプリがインストールされてたら起動するリンク」(URL Scheme)を実装したのでメモ

バージョン情報

iOSアプリの作成にはクロスプラットフォームフレームワークのIonicを使用しました

  • angular@5.0.1
  • ionic-angular@3.9.5
  • ionic-plugin-deeplinks@1.0.20
  • ionic-native/deeplinks@4.20.0

実装した要件

  1. Webサイトのボタンをクリックした時に端末にアプリがインストールされていれば、アプリを開く
  2. Webサイトのボタンをクリックした時に端末にアプリがなければ、AppStoreに遷移する

上記の要件をURL Schemeを使用することで実装していきます。

なお、要件2については、Webサーバ上にapple-app-site-associationファイルを置けない制約があったため、実装できていません。

実装

URL Schemeを実装するためには、主にライブラリのインストールとAppComponentクラスの改修が必要になります。

ライブラリのインストール

Deeplinks - Ionic Documentation

IonicのプラグインDeeplinksを使用して、URL Scheme機能を実装していきます。

URL Schemeで開きたいアプリのプロジェクト配下に移動して、下記のコマンドを実行してインストールを行います。

なお、今回の例ではURL Schemeを以下の条件で設定します

  • URL_SCHEME: sample
  • DEEPLINK_SCHEME: https
  • DEEPLINK_HOST: sample.com

コマンドのオプション引数はURL Schemeの設定で変わるので注意。

$ ionic cordova plugin add ionic-plugin-deeplinks --variable URL_SCHEME='sample' --variable DEEPLINK_SCHEME=https --variable DEEPLINK_HOST='sample.com'

上記のコマンドを実行すると、プラグインがインストールされ、package.jsonに以下の設定が入ります。

"ionic-plugin-deeplinks": {
   "URL_SCHEME": "sample",
   "DEEPLINK_SCHEME": "https",
   "DEEPLINK_HOST": "sample"
}

続いて、ionic-nativeプラグインをインストールします。

npm install @ionic-native/deeplinks

これでライブラリのインストールは完了です。

AppComponentの改修

アプリのAppComponentを改修することによって、URL Schemeをクリックした時にアプリのどの画面に遷移するかを設定することができます。

Custom URL Schemeのルーティング設定

Webサイト側の設定

今まではアプリの設定だけをしていましたが、これからは、URL Schemeを表示するWebサイトの設定に入ります。

といってもWebサイト側では、URL Schemeに遷移する処理を書くだけでOKです。他の設定は必要がありません。

本記事の設定だと、URL Schemeは下記のようになります。

sample://

上記のURL Schemeにアクセスするだけで、iOS端末にアプリがインストールされている場合はアプリを開き、アプリがインストールされていなければ、ダイアログでアプリを開けない旨を知らせてくれます。

これでURL Schemeの実装は完了です。

今回は、業務上の制約により『アプリがインストールされていなければ、AppStoreを開く」といった処理を実施しないため、Associated Domainsの設定もapple-app-site-associationファイルの設定も実施する必要はありません。

参考サイト

Deeplinking in Ionic Apps - Ionic Blog

Ionic+Cordovaでuniversal linkの実装(ios版) - Qiita

【Python】[Selenium]クレジットカードの利用明細をダウンロードする

Pythonを練習しようパート1

Pythonの入門書を読んでから数ヶ月...全くパイソンを書かない日々が続いたので、勉強がてらクレジットカード利用明細のダウンロードをやってみました

出典

ゼロからはじめるPython(49) Pythonでブラウザ自動操縦してカード明細を自動でダウンロードしよう(その1) | マイナビニュース

ゼロからはじめるPython(50) Pythonでブラウザ自動操縦してカード明細を自動でダウンロードしよう(その2) | マイナビニュース

上の記事を見ながらカタカタと実装していきます。

バージョン情報

PCはmacOS Mojave(10.14.6)

  • selenium: 3.141.0
  • chrome: 76.0
  • ChromeDriver: 76.0
  • Python 3.7.3

SeleniumとChromeDriverのインストール

ChromeブラウザをPythonから操作することによってクレジットカードの利用明細をダウンロードするので、 UIテスト自動化ツールのSeleniumとChromeDriverをインストールが必要になります。

まずpip installコマンドでSeleniumをインストール。

$ pip install selenium

Chrome driverについては手動でPATHを通すのが面倒だったので、pip installではなくHomebrewでインストールをしました。

$ brew install chromedriver

brew install chromedriverを実行した結果下記のエラーメッセージが出力されて失敗。

Error: No available formula with the name "chromedriver" 
Found a cask named "chromedriver" instead. Try
  brew cask install chromedriver

エラーメッセージに従って下記のコマンドでChrome driverのインストールを実行し、成功。

$ brew cask install chromedriver

テストプログラムの実行

環境構築(SeleniumとChromeDriverのインストール)ができていることを確認するために、下記のテストプログラムを実行します。

  • hello.py
import time
from selenium import webdriver

# Chromeを起動する
driver = webdriver.Chrome()
# Googleのページを開く
driver.get('https://www.google.com')
# ページが開くまで待つ
time.sleep(5)
# 検索ボックスのオブジェクトを得る
q = driver.find_element_by_name('q')
# 検索ボックスにキーを送信する
q.send_keys('まちカドまぞく')
# フォームを投稿する
q.submit()
# 結果を30秒表示して終了する
time.sleep(30)
driver.quit()

テストプログラムの内容としては、Chromeブラウザを開いてまちカドまぞくについて検索するというもの。

下記のコマンドでテストプログラムを実行します。

$ python hello.py

実際にテストプログラムを実行した結果、下記のエラーが表示されて失敗しました。

selenium.common.exceptions.SessionNotCreatedException: Message: session not created: This version of ChromeDriver only supports Chrome version 76

Chrome driverとChromeのバージョンが一致していないことが原因のエラーとなります(ChromeDriverのバージョンが76なのに、Chromeブラウザのバージョンが72だった)。

Chromeのバージョンを76にアップグレードして、再度テストプログラムを実行したところ成功しました

f:id:l08084:20190827234011p:plain
テストプログラム成功

クレジットカード利用明細のダウンロード

では環境構築が完了したので、実際にクレジットカード明細のダウンロードをやっていきます。

注意事項

明細取得に進む前に注意事項ですが…自動でログインするのを禁じているサイトなどもあるため、事前にサイトの利用規約を読んでおきましょう

実装

ドコモユーザーでもないのにDカードを使っているので、Dカードの利用明細を取得するコードを書いていきます。

ドコモのクレジットカード(Dカード)の利用明細をダウンロードするコード

上のコードでは、find_element_by_nameでエレメントのname属性をキーに、ユーザID欄とパスワード欄を取得して、値を入力してログインしてから、
利用明細ページで利用明細のCSVをダウンロードするメソッドを呼び出す...といった処理をやっています。

ユーザIDとパスワードについては、Gitに間違えてアップしたら大変なので、.gitignoreに設定しているファイルに定義しています。

  • d_credentials.py
ID = "XXXXXXXXX"
PASS = "XXXXXXXXX"

上のプログラムを実行すると、Chromeブラウザが立ち上がって、ユーザIDとパスワードを自動で入力して明細のCSVをダウンロードしてくれます。

$ python download_d-card.py

f:id:l08084:20190903214229p:plain
プログラム実行時

遭遇したエラー

selenium.common.exceptions.ElementClickInterceptedException: Message: element click intercepted: Element <a href="javascript:void(0)" onclick="doSubmitForm(document.download_form);">...</a> is not clickable at point (891, 803). Other element would receive the click: <td scope="col" colspan="4"></td>

Pythonで利用明細のダウンロードリンクをクリックしようとする過程で上記のエラーに遭遇しました。

利用明細CSVのダウンロードリンク<a href="javascript:void(0)" onclick="doSubmitForm(document.download_form);">...</a>が他のエレメントに邪魔されてクリックできないというエラーになります。

最終的にCSVダウンロードリンクをクリックするのを諦めて、execute_scriptで直接DカードのWebサイト側で定義しているCSVのダウンロードメソッドdoSubmitForm(document.download_form)を呼び出すことで解決しました。

参考サイト

Selenium利用時のトラブルシューティング方法[クリック編]

おわりに

自慢じゃないけどクレジットカードの明細みるの1年ぶりくらいです...(家で見たことないから実家に届いてるのかもしれない

Angular + Firebase でアカウント登録画面の作成

AngularでFirebase認証(その1) Firebaseのセットアップ - 中安拓也のブログ

AngularでFirebase認証(その2) Angular Materialを使ったログイン画面の作成 - 中安拓也のブログ

AngularでFirebase認証(その3) Firebase Authentication の呼び出し - 中安拓也のブログ

上の3記事で作成したAngular + FirebaseのWebアプリにアカウント登録機能を追加していきます。

メールアドレスとパスワードを使用したログイン機能については上の記事で既に実装済みです。

アカウント登録画面の作成

CSSフレームワークのAngular Materialを使ってアカウント登録画面の見た目から作っていきます。

Angular CLIベースのプロジェクトなので、下記のコマンドでアカウント登録画面Componentの一式を作成できます。

ng generate component sign-up

CLIで作成したファイルにレイアウトとバリデーションの処理を書いていきます。

  • sign-up.component.html
<app-header></app-header>
<div class="wrapper">
  <mat-card class="sign-up-card">
    <mat-card-header>
      <mat-card-title class="sign-up-title">Create an account</mat-card-title>
    </mat-card-header>
    <mat-card-content>
      <form (ngSubmit)="onSubmit()" class="sign-up-form" [formGroup]="signUpFormGroup">
        <mat-form-field>
          <input matInput placeholder="user name" id="userName" formControlName="userName" required>
          <mat-error *ngIf="userNameControl.invalid">{{getErrorMessageToUserName()}}</mat-error>
        </mat-form-field>
        <mat-form-field>
          <input matInput placeholder="email" id="email" formControlName="email" required>
          <mat-error *ngIf="emailControl.invalid">{{getErrorMessageToEmail()}}</mat-error>
        </mat-form-field>

        <mat-form-field>
          <input [type]="hide ? 'password' : 'text'"
            matInput placeholder="password" id="password" formControlName="password" required>
          <mat-icon matSuffix (click)="hide = !hide">{{hide ? 'visibility' : 'visibility_off'}}</mat-icon>
          <mat-error *ngIf="passwordControl.invalid">{{getErrorMessageToPassword()}}</mat-error>
        </mat-form-field>
        <mat-form-field>
          <input [type]="hideConfirm ? 'password' : 'text'"
            matInput placeholder="confirm password" id="confirmPassword" formControlName="confirmPassword" required>
          <mat-icon matSuffix (click)="hideConfirm = !hideConfirm">{{hideConfirm ? 'visibility' : 'visibility_off'}}</mat-icon>
          <mat-error *ngIf="confirmPasswordControl.invalid">{{getErrorMessageToConfirmPassword()}}</mat-error>
        </mat-form-field>
        <button type="submit" class="sign-up-button" mat-raised-button [disabled]="!signUpFormGroup.valid" color="primary">Create your account</button>
      </form>
    </mat-card-content>
  </mat-card>
</div>

アカウント登録画面のHTMLファイルです。アカウント名、メールアドレス、パスワードと確認用パスワードのフォームを設定しています。

  • sign-up.component.ts
import { Component, OnInit } from '@angular/core';
import {
  FormGroup,
  FormControl,
  FormBuilder,
  Validators
} from '@angular/forms';
import { AngularFireAuth } from '@angular/fire/auth';
import { Router } from '@angular/router';
import { CustomValidator } from '../validation/custom-validator';

@Component({
  selector: 'app-sign-up',
  templateUrl: './sign-up.component.html',
  styleUrls: ['./sign-up.component.scss']
})
export class SignUpComponent implements OnInit {
  // FormGroup定義
  public signUpFormGroup: FormGroup;
  public userNameControl: FormControl;
  public emailControl: FormControl;
  public passwordControl: FormControl;
  public confirmPasswordControl: FormControl;

  constructor(
    private fb: FormBuilder,
    private afAuth: AngularFireAuth,
    private router: Router
  ) {}

  public ngOnInit() {
    this.createForm();
    this.userNameControl = this.signUpFormGroup.get('userName') as FormControl;
    this.emailControl = this.signUpFormGroup.get('email') as FormControl;
    this.passwordControl = this.signUpFormGroup.get('password') as FormControl;
    this.confirmPasswordControl = this.signUpFormGroup.get(
      'confirmPassword'
    ) as FormControl;
  }

  /**
   * アカウント登録ボタン押下時に呼び出し
   *
   */
  public onSubmit() {
  }

  /**
   * ユーザーネームフォームにバリデーションエラーメッセージを表示
   *
   */
  public getErrorMessageToUserName() {
    return this.userNameControl.hasError('required')
      ? 'You must enter a value'
      : '';
  }

  /**
   * Eメールフォームにバリデーションエラーメッセージを表示
   *
   */
  public getErrorMessageToEmail() {
    return this.emailControl.hasError('required')
      ? 'You must enter a value'
      : this.emailControl.hasError('email')
      ? 'Not a valid email'
      : '';
  }

  /**
   * パスワードフォームにバリデーションエラーメッセージを表示
   *
   */
  public getErrorMessageToPassword() {
    return this.passwordControl.hasError('required')
      ? 'You must enter a value'
      : '';
  }

  /**
   * 確認用パスワードフォームにバリデーションエラーメッセージを表示
   *
   */
  public getErrorMessageToConfirmPassword() {
    return this.confirmPasswordControl.hasError('required')
      ? 'You must enter a value'
      : this.confirmPasswordControl.hasError('notMatchPassword')
      ? 'Password and confirm password do not match'
      : '';
  }

  /**
   * フォーム設定の作成
   *
   */
  private createForm() {
    this.signUpFormGroup = this.fb.group(
      {
        userName: ['', [Validators.required]],
        email: ['', [Validators.required, Validators.email]],
        password: ['', [Validators.required]],
        confirmPassword: ['', [Validators.required]]
      },
      {
        // パスワードと確認パスワードが一致しているか確認するバリデーション
        validator: CustomValidator.matchPassword
      }
    );
  }
}

アカウント登録画面のコンポーネントクラスです。Angularのリアクティブフォームの設定とバリデーション処理を記載しています。

パスワードと確認用パスワードが一致しているか確認するバリデーションは下記のファイルに分けて書いています。

  • custom-validator.ts
import { AbstractControl } from '@angular/forms';

export class CustomValidator {
  // パスワードと確認用パスワードが一致するかチェック
  static matchPassword(ac: AbstractControl) {
    const password = ac.get('password').value;
    const passwordConfirm = ac.get('confirmPassword').value;
    if (password !== passwordConfirm) {
      ac.get('confirmPassword').setErrors({ notMatchPassword: true });
    }
  }
}
  • sign-up.component.scss
.wrapper {
  display: flex;
  justify-content: center;
  align-items: center;
  margin-top: 15%;

  .sign-up-card {
    width: 500px;
  }

  .sign-up-form {
    display: flex;
    flex-direction: column;

    .sign-up-button {
      margin-top: 20px;
    }
  }

  .sign-up-title {
    font-weight: bold;
    font-size: 22px;
    color: #3f51b5;
  }
}

アカウント登録画面のCSSファイルです。ログイン画面のものをそのまま流用しています。

f:id:l08084:20190825154003p:plain
アカウント登録画面

ここまでのコードを実行すると、上記の画面が表示されます。

アカウント登録

AngularFireライブラリを使って、アカウント登録処理を書いていきます。

  • sign-up.component.ts
  /**
   * アカウント登録ボタン押下時に呼び出し
   *
   */
  public onSubmit() {
    this.afAuth.auth
      // Firebaseのアカウント登録処理の呼び出し
      .createUserWithEmailAndPassword(
        this.emailControl.value,
        this.passwordControl.value
      )
      .then(created => {
        const newUser = created.user;
        // 作成したアカウントにdisplayNameを設定する
        newUser
          .updateProfile({
            displayName: this.userNameControl.value,
            photoURL: ''
          })
          .then(() => {
            // アカウント登録処理が成功したらログイン画面に戻る
            this.router.navigate(['/login']);
          });
      });
  }

まず Firebase AuthenticationのcreateUserWithEmailAndPasswordを呼び出してアカウントを登録します。

createUserWithEmailAndPasswordが成功した後に、updateProfileを呼び出してアカウント名の設定をしています。アカウント名の設定が成功したら、ログイン画面への遷移処理を呼び出します。

動作確認

f:id:l08084:20190825154003p:plain
アカウントの登録

試しにアカウントを登録してみます。

f:id:l08084:20190825162722p:plain
作成したアカウントでログイン

ログイン画面に登録したアカウントのメールアドレスとパスワードを入力すると...

f:id:l08084:20190825162842p:plain
ログイン成功

ログインに成功します。

バージョン情報

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

参考サイト

Firebase でユーザーを管理する

【Ionic(Angular)】Hammer.jsを使っている画面で垂直スクロールができなくなる問題

仕事中に遭遇した障害についてのメモ。iOS13(ベータ版)とAndroid 9で検知した。

発生事象

Ionic(Angular)のGestureを使ってピンチイン・ピンチアウトによるズームを有効にしている画面で、縦方向のスクロールができなくなる障害が発生した。

原因

Ionic(Angular)のGesture機能で呼び出しているライブラリ、Hammer.jsが障害の原因となる。

特定のiOS, Androidのバージョンでスクロールができなくなることを確認した。

対応

Gesturte機能(Hammer.js)を使用しているタグに対して、[style.touch-action]="'pan-y'"を追加することで障害を改修した。

Hammer.jsによって無効になった縦方向のタッチアクションの検出をCSS[style.touch-action]="'pan-y'"で有効にすることによって、スクロールを検知できるようにしている。

  • 対応コードの例

pinchZoomはHammer.jsを使用しているAngularのカスタムディレクティブとなる。

<div [style.touch-action]="'pan-y'" pinchZoom>

バージョン情報

  • Angular: 5.0.1
  • Ionic: 3.9.5

参考サイト

Hammer blocking scroll · Issue #10541 · angular/angular · GitHub

Scrolling disabled on Hammer.js element · Issue #1014 · hammerjs/hammer.js · GitHub