L08084のブログ

技術記事の執筆は祈り

Angularがデータの変更を検知してくれないのでChange Detectionを呼ぶ

障害対応でAngularのChange Detectionをコントロールするクラス(ChangeDetectorRef)を使用する機会があったのでメモ

発生した障害

クリックでインクリメントする値と複数のngIfを組み合わせて、アプリの操作方法を説明するチュートリアルを作成していたが、値は正常にカウントアップされているのにngIf(チュートリアル)が切り替わらない...という障害がときどき発生した

  • ソースのイメージ
<!-- クリックするとチュートリアルが切り替わる -->
<div (click)="nextStep()">
    <!-- stepの値が増えてもずっと、tutorial-1だけが表示される障害 -->
    <div class="common-tutorial-img" >
      <div class="tutorial-1" *ngIf="step === 1"></div>
      <div class="tutorial-2" *ngIf="step === 2"></div>
      <div class="tutorial-3" *ngIf="step === 3"></div>
    </div>
</div>

上記のソースのイメージで、発生した障害を説明するとstep変数は1, 2, 3...といった感じにインクリメントされているのに、ずっと<div class="tutorial-1" *ngIf="step === 1"></div>だけが表示されるといった事象がおきた。

対応内容

step変数の値が変更されているのをAngularが検知して再描画してくれないのが原因だと考えたため、step変数の値が増えるたびにChangeDetectorRef#detectChanges()を毎回呼び出して、モデル(step変数)の値が変更されるたびに画面を再描画するように修正した。

終わりに

step変数の値の渡しかたを工夫してあげれば、ChangeDetectorRef#detectChanges()を呼び出さなくても自動でChange Detectionがはしった気がしないでもない

バージョン

  • Angular v4
  • ionic-angular: 3.9.5

参考サイト

https://angular.io/api/core/ChangeDetectorRef

[Angular] コンポーネントを強制的に再描画する方法 │ Web備忘録

日本語訳:Angular 2 Change Detection Explained - Qiita

【バグ対応メモ】[Ionic]iOS 12.2以降の端末でスクロールができなくなった

f:id:l08084:20190623202512p:plain

業務で作成したiOSアプリの障害についてのメモ

障害内容

iOS 12.2以降の端末でIonic v3.9.3で作成したiOSアプリを動かしたら、スクロールができなかった。

改修方法

Ionic v3.9.5以前のバージョンによるバグが原因の障害となる。ionic-angularのバージョンを3.9.5までアップグレードすると、スクロールできるようになる。

参考サイト

詳細は、Ionicのv 3.9.5のドキュメントを参照のこと

Release 3.9.5 · ionic-team/ionic-v3 · GitHub

【Angular】GitHub Pagesの更新方法がわからない

はじめに

Angularで作成したサイト(GitHub Pagesにデプロイ)を、更新したくなった時はどうすればいいんだろう?私、わかりません...という記事です。ググるなり Stack Overflowで聞くなりしろよという話なんですが

バージョン情報

WebサイトはAngular v7で作成しました、Web APIとかはない。

そもそもどうやってデプロイしたのか?

モールス信号翻訳

上記のサイトを、angular-cli-ghpagesというライブラリを利用してデプロイしました(デプロイ対象のブランチはgh-pages)。

デプロイするにあたって実行したコマンドは下記となります。

$ npm install -g angular-cli-ghpages
$ ng build --prod --base-href "https://l08084.github.io/morse-code-translate-website/"
$ git checkout -b gh-pages
$ git push origin gh-pages
$ ng build --prod --base-href "https://l08084.github.io/morse-code-translate-website/"
$ ngh --dir=dist/morse-code-tweet

結局どうしたのか?

普通に修正したい資源を更新して、gh-pagesブランチにプッシュするだけだとサイトの表示が変になってしまったので、適当なやり方でサイトの更新をしました。正しい更新方法が知りたい。

  • 適当なやり方
    1. ローカルとリモートのgh-pagesブランチを削除する
    2. 資源を更新する
    3. 再度、はじめからデプロイの手順をやる

【JavaScript】Angularでモールス信号を翻訳するWebサイトを作成した

f:id:l08084:20190421173047g:plain

はじめに

Angularを使用して、日本語・英語をモールス信号に変換するWebサイト「モールス信号 翻訳」を作成しました。

↓のURLから実際に触ることができます

https://l08084.github.io/morse-code-translate-website/

GitHubにソースコードもアップしています

github.com

使用ライブラリ・言語のバージョン情報

TypeScript(JavaScriptのスーパーセット)のフレームワークであるAngular(v7)を使用して作成しました。 CSSフレームワークはAngular Materialを利用しています。

  • Angular@7.3.6
  • AngularCLI@7.3.6
  • Angular Material@7.3.5
  • rxjs@6.3.3
  • TypeScript@ 3.2.4
  • webpack@4.29.0

「モールス信号 翻訳」の機能

「モールス信号 翻訳」には、日本語・英語をモールス信号に変換する機能と、モールス信号を日本語・英語に逆変換する機能があります。

アカウント登録などの機能はないためAPIがない、いわゆる静的サイトになっています。(フロントサイドで完結するから作るのが楽だったということを言いたい)

実装

ソースコードの中身について解説していきます

ディレクトリ構成

$ tree -I node_modulesコマンドで出力したディレクトリ構成(一部ファイル省略)。

.
├── src
│   ├── app
│   │   ├── app.component.html
│   │   ├── app.component.scss
│   │   ├── app.component.ts
│   │   ├── app.module.ts
│   │   ├── components
│   │   │   ├── description
│   │   │   │   ├── description.component.html
│   │   │   │   ├── description.component.scss
│   │   │   │   └── description.component.ts
│   │   │   ├── header
│   │   │   │   ├── header.component.html
│   │   │   │   ├── header.component.scss
│   │   │   │   └── header.component.ts
│   │   │   ├── input
│   │   │   │   ├── input.component.html
│   │   │   │   ├── input.component.scss
│   │   │   │   └── input.component.ts
│   │   │   ├── output
│   │   │   │   ├── output.component.html
│   │   │   │   ├── output.component.scss
│   │   │   │   └── output.component.ts
│   │   │   ├── select
│   │   │   │   ├── select.component.html
│   │   │   │   ├── select.component.scss
│   │   │   │   └── select.component.ts
│   │   │   └── share-button
│   │   │       ├── share-button.component.html
│   │   │       ├── share-button.component.scss
│   │   │       └── share-button.component.ts
│   │   └── service
│   │       ├── convert.service.spec.ts
│   │       └── convert.service.ts
│   ├── assets
│   │   └── mapping.json
│   ├── index.html
│   ├── styles.scss
└── tslint.json

コンポーネントの構成

ディレクトリ構成をみればわかる通り(?)、ルートコンポーネントに下記のコンポーネントがぶら下がっている構成になっています。

  • header コンポーネント
    • ただのヘッダー
  • select コンポーネント
    • ドロップダウンのコンポーネント。変換方式を選択する
  • input コンポーネント
    • 変換対象のテキストを入力するテキストエリアのコンポーネント
  • output コンポーネント
    • 変換結果が表示されるテキストエリアのコンポーネント
  • share-button コンポーネント
    • facebook、はてブ、Twitterのシェアボタンを表示するコンポーネント
  • description コンポーネント
    • サイトの使い方などを説明するテキストを表示しているコンポーネント

上記のコンポーネントに加えて、テキスト⇄信号 変換のビジネスロジックが記述されているConvert サービスクラスがあります

ルートコンポーネントの実装

ルートコンポーネント(AppComponent)では、子コンポーネントにAngularの@Input@Outputを使用して値の受け取りと受け渡しを行なっています。

ルートコンポーネントのテンプレートファイル

input コンポーネントの実装

input コンポーネントは、翻訳したいテキスト・モールス信号を入力するテキストエリアの役割を持っています。

f:id:l08084:20190421193154p:plain
input コンポーネント

input コンポーネントでは、下記の処理を行なっています。

  • 翻訳形式によって、placeholderとヒント文を切り替える
  • テキストエリアの値が変更されてから500ミリ秒経過した後に、翻訳サービスロジック(Convert サービスクラス)を呼び出す

input コンポーネントクラス

convert サービスクラスの実装

テキストからモールス信号に変換するロジックですが、Mapデータ構造を使用しました。具体的には下記の手順になります。

  1. JSONファイルでモールス信号と、カタカナやアルファベットのリストを作成する
  2. keyにカタカナとアルファベット、valueにモールス信号を設定したMap型のデータを作成する
  3. 入力テキストを1文字ずつ分割する
  4. 2で作成したMapを経由することでテキストをモールス信号に変換する...といった流れです(逆変換の場合はこの逆です)

share-buttonコンポーネントの実装

f:id:l08084:20190421175510p:plain
share-button コンポーネント

数字がずっと0から変わらない気がする...share-buttonコンポーネントの実装、ひいてはSNSシェアボタンの表示方法ですが、各種SNSの開発者サイトにいくとシェアボタンのコードが提供されているので、それをコピペして貼り付けるだけです(開発者アカウントの登録などは必要ありません)。

注意点としては、SNSの開発者サイトだと、ボタンのHTMLタグとスクリプト(script)タグが一緒くたにされているため、コピペするときは分割する必要があります。ボタンのHTMLについては、コンポーネントのHTMLにコピペして、<script>については、index.htmlの方に貼り付ける必要があります。

テンプレートファイルには、SNSシェアボタンのHTMLタグだけを貼り付けます。

share-buttonコンポーネントのテンプレートファイル

SNSシェアボタンの<script>index.htmlに貼り付けます

  • index.html
<!doctype html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <title>モールス信号翻訳</title>
  <base href="/">

  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta name="description" content="日本語(ひらがな・カタカナ)や英語(アルファベット)をモールス信号に変換するサービスです。">
  <meta name="keywords" content="モールス信号,モールス符号,モールス,和文モールス,和文モールス符号,和文モールス信号">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
  <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
  <script async defer crossorigin="anonymous" src="https://connect.facebook.net/ja_JP/sdk.js#xfbml=1&version=v3.2">
  </script>
  <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
  <script type="text/javascript" src="https://b.st-hatena.com/js/bookmark_button.js" charset="utf-8" async="async">
  </script>
</head>

<body>
  <app-root></app-root>
</body>

</html>

デプロイ

GitHub Pageへのデプロイ方法について

GitHub Pagesにデプロイする

How to deploy an Angular 7 app to Github Pages – Code Sketch – Medium

Angular 7系以降のデプロイ方法については、上のサイトが詳しいです。

↓は自分がGitHub Pagesにデプロイした時のコマンドです。

$ npm install -g angular-cli-ghpages
$ ng build --prod --base-href "https://l08084.github.io/morse-code-translate-website/"
$ git checkout -b gh-pages
$ git push origin gh-pages
$ ng build --prod --base-href "https://l08084.github.io/morse-code-translate-website/"
$ ngh --dir=dist/morse-code-tweet

終わりに

休日にテキストとモールス信号のマッピング表を作ってると虚無感がすごい。「あ・い・う・え・お」とか打つ必要があるので(虚無感というか虚無そのものだった気がする)

【Git】SouceTreeでpushが失敗する「hint: Updates were rejected because the tag already exists in the remote.」

SourceTreeでgit pushを実行した時に下記のようなメッセージが表示されて失敗した。

git -c diff.mnemonicprefix=false -c core.quotepath=false --no-optional-locks -c credential.helper= -c credential.helper="C:/Users/XXX/AppData/Local/ATLASS~1/SOURCE~1/GIT_EX~1/GIT-CR~1.EXE" push -v --tags origin master:master
Pushing to https:/XXX/gitlab/XX/XXX.api
To https://XXX/gitlab/XX/dXXX.api


updating local tracking ref 'refs/remotes/origin/master'
error: failed to push some refs to 'https:/XXX/gitlab/XX/XXX.api'
hint: Updates were rejected because the tag already exists in the remote.

解決方法

git pull --tags

ターミナルを立ち上げて上記コマンドを実行すると、git pushができるようになる。

参考サイト

https://stackoverflow.com/questions/31929667/updates-were-rejected-because-the-tag-already-exists-when-attempting-to-push-i

【Angular】NgRx入門

はじめに

AngularでFluxを実現するために採用されるライブラリについては、NgRxがデファクトスタンダードになりつつあるのではないか.....という話を結構前に聞いたのでNgRxの学習をそろそろ始めることにする。

NgRx以外のFluxライブラリを使ったことは何度かあるので、Fluxの概念については理解しているつもり

参考サイト

ngrx.io

バージョン情報

  • angular@7.3.6
  • ngrx/store@7.3.0

環境構築

Angular CLIを利用して、Angularプロジェクトを作成した後に@ngrx/storeライブラリをインストールする。

https://ngrx.io/guide/store/install

Angular CLIのバージョンが6以上の場合は、npmやyarnで@ngrx/storeライブラリをインストールする以外にも、ng addコマンドを使用して(Angular CLI経由で)@ngrx/storeライブラリをインストールすることができる。

$ ng add @ngrx/store

ng addコマンドで@ngrx/storeライブラリをインストールすると、app.module.tsの設定と、src/app/reducers/index.tsの新規作成まで自動でやってくれる。

ただ、今回は自分で設定をしたかったので、npm i @ngrx/storeでインストールを実行した。

開発

f:id:l08084:20190319220128p:plain
完成したカウンターアプリ
公式サイトで@ngrx/storeのチュートリアルアプリとして用意されているカウンターアプリを作成したので、解説する。(なお、公式サイトのものとはディレクトリ構成が異なっている)。

ディレクトリ構成

作成したカウンターアプリのディレクトリ構成を$ tree -I node_modulesコマンドで出力した(一部ファイルは省略している)。

├── src
│   ├── app
│   │   ├── app.component.html
│   │   ├── app.component.ts
│   │   ├── app.module.ts
│   │   └── components
│   │       └── my-counter
│   │           ├── action
│   │           │   └── counter.actions.ts
│   │           ├── reducer
│   │           │   └── counter.reducer.ts
│   │           └── view
│   │               ├── my-counter.component.html
│   │               └── my-counter.component.ts
└── ・・・

コード

まず、FluxのActionを担当するファイルであるcounter.actions.tsから解説する。今仕事で使っているFluxライブラリ(自社製)とはすでに大きく違っている。

カウンターアプリのイベントである"加算"、"減算"、"リセット"をそれぞれアクションとして定義している。

今の仕事で使っているFluxライブラリだと、アクションの定義だけでなくWebAPIの呼び出しや、実際にStateの値を変更するStoraクラスの呼び出しなど(ActionをStoreに渡す)をやっていたため、結構な違いを感じてとまどう。

カウンターアプリのアクションクラス

続いてReducerを担当しているcounter.reducer.tsについて。今仕事で使っているFluxライブラリだとStoreでStateの変更をおこなっているが、NgRxでは渡されたアクションに応じてReducerが新しいStateを返す形式になっているらしい。

export const initialState = 0;の部分でStateの初期化を行っている。

カウンターアプリのReducer

ルートモジュール設定ファイルであるapp.module.tsについて。NgRxに関連している設定として、imports[]でReducerの登録を行っている(StoreModule.forRoot({ count: counterReducer }))のと、Redux Dev Toolの設定をしている(StoreDevtoolsModule.instrument({}),)。

モジュールファイル

カウンターコンポーネントのテンプレートファイル。カウンター値の表示と、加算、減算、リセットボタンを表示を担当している。

stateの値であるcount$Observable型なのでasyncパイプを使用して値の表示を行っている。

カウンターコンポーネントのテンプレートファイル

selectstateの値の読み込みと、加算、減算、リセットのアクションをディスパッチして、Reducerを呼び出すメソッドをそれぞれ定義している。

カウンターコンポーネントのコンポーネントクラス

ルートコンポーネントでは、カウンターコンポーネントを定義しているだけ

ルートコンポーネントのテンプレートファイル

おわりに

少し触ってみた感想としては、NgRxはReduxよりのFluxライブラリなのでは...という印象を受けた。Redux使ったことないので習得に時間がかかりそう

【Python3】特徴的だと思った機能まとめ その4

引き続き、『入門 Python 3』を読んでいて印象に残った箇所をメモに残していく

前回の記事

【Python3】特徴的だと思った機能まとめ その3 - L08084のブログ

バージョン情報

  • Python 3.7.2

出典元

https://www.oreilly.co.jp/books/9784873117386/

オライリーの『入門 Python 3』

学習メモ

\による長い行の分割

バックスラッシュ(\)で行を分割することができる。

>>> age = 15

# 三項演算子の行を分割して読みやすくしている
>>> name = 'みゃー姉' \
... if age >= 18 \
... else 'アカネ'

>>> name
'アカネ'

内包表記

内包表記とは、イテレータ*1からPythonのデータ構造をコンパクトな構文で作成できる機能を指す。

内包表記で作成できるデータ構造として、リスト、辞書、集合などがあり、それぞれリスト内包表記、辞書内包表記、集合内包表記といった感じに命名されている。

リスト内包表記

リストをシンプルな構文で作成できる。リスト内包表記の文法は下記の通りとなる。

  • [expression for item in iterable]
    • リスト内包表記の最も単純な形式
  • [expression for item in iterable if condition]
    • 条件を追加した形式
# 単純な形式のリスト内包表記
>>> numList = [num for num in range(1, 7)]
>>> numList
[1, 2, 3, 4, 5, 6]

# 0 ~ 7の値を取り出して二乗した後、リスト化している
>>> squaringList = [i * i for i in range(0, 8)]
>>> squaringList
[0, 1, 4, 9, 16, 25, 36, 49]

# 条件を追加したリスト内包表記
>>> evenList = [j for j in range(0, 8) if j % 2 == 0]
>>> evenList 
[0, 2, 4, 6]
辞書内包表記

ディクショナリも内包表記を使用してワンライナーで作成することができる。{key_item : value_item for item in iterable}

# リストを定義
>>> marshalList = ['ランヌ', 'ミュラ', 'ダヴー', 'ベルティエ', 'ネイ']

# 内包表記でdictを作成
>>> marshal_dict = {i : marshalList[i] for i in range(0, 5)}
>>> marshal_dict 
{0: 'ランヌ', 1: 'ミュラ', 2: 'ダヴー', 3: 'ベルティエ', 4: 'ネイ'}

*1:要素を順番に取り出すことのできるオブジェクトのこと。リスト(配列)、辞書、集合、タプルなど