Ionicで電卓を作成したので振り返る(仕様編)の続きです。
今回は、Componentを使った履歴表示部分の作成についてです。
主に以下の内容について書きます。
・ボトムアップ式のリスト実現方法
・変更検知と謎のsetTimeout
・アニメーション
作ったComponentの説明

履歴表示用にbottomup-listという名前のComponentを作成しました。
ソースコードは以下。
https://github.com/scrpgil/xcalculator/tree/master/src/components/bottomup-list
<bottomup-list [calcs]="calcs" [len]="calcs.length"></bottomup-list>
Inputとしてcalcsとlenがあります。
calcs:履歴の一覧。オペレーター(+,-,✕,÷等)と入力値をメンバに持つICalc構造体の配列。
len:calcsの長さ
ICalc構造体は下のような感じ。
export interface ICalc{
Buffer: string;
Operator: string;
Decimal: boolean;
}
そしてcalcsは以下のように、ICalcの配列。
public calcs:ICalc[];
ボトムアップ式のリストの実現について
履歴表示部はLINEやFacebook MessagerのUIのようにBottomup式の表示を行います。が、しかし、中々実現方法がわからず、結果、かなり苦しい実装をしています。
苦しい実装になったのは、履歴表示部の高さは可変というところにあります。今回、参考にしたのはMicrosoft Bot Frameworkのチャット表示の処理でmargin-topを指定するというものでした。
さらにmargin-topの計算をしやすくするために履歴部の数値表示についてはheight:24px固定となっています。これで、24 * 履歴の数といった計算ができます。
ちなみに、高さが決まっていれば「position:absolute」とかの方法もあったっぽいです。ここらへんは自分の知識不足なだけの可能性もありmargin-topを指定する以外の方法もあるかもしれません。
変更検知と謎のsetTimeout
次に、変更検知になります。新規に履歴が追加されるたびに先程のmargin-topを計算し直しする必要があります。これについては、bottomup-listコンポーネントでOnChangesクラスを継承しngOnChanges関数を使って実現しています。
こうすることで、ComponentのInputに変更が会った場合、変更を検知することができるようになりました。
が、しかし、ここでも苦しい実装があります。なにかと言いますとどうにもcalcsの方は変更検知してくれません。どうも、構造体や配列へ要素を追加するだけだと変更検知してくれないようです。
※ngForの方は動きます。
そこで今回は、変更検知をするためだけにinputで配列の長さ(length)も設定するというなんとも奇々怪々なことをしています。一様これで変更検知は動くようになります。
謎のsetTimeout
もう一つ変更検知後のmargin計算処理に対して全体をsetTimeoutで遅らせるなんてことをしています。これは、calcsの値が変更されて実際に履歴が描画されるまで少しラグがあったので行っています。
このsetTimeoutがないと、すぐにmargin-topの計算をおこなってしまい1要素増える前の状態で再度計算を行っていまうという事象に見舞われました。これもでたり出なかったりで対応としても果たしてこれでよいのか?疑問なところです。
普通に考えると悪いのでいつか誰かに正解を聞いてみたいです。
アニメーション
アニメーションは2種類の方法で実現しています。ひとつはCSSを使う方法、もうひとつはObservableです。Angularのアニメーションは使いたかったけど挫折しました。
CSSを使っているところ
履歴表示部に以下のCSSを適用しています。幸い今回の履歴表示部の動作はmargin-topを変更するという方法を使っているため、CSSによるアニメーションが可能になっています。
.history-list{
height:100%;
overflow-y: scroll;
transition-duration:300ms;
}
Observableを使っているところ
CSSによるアニメーションはscrollTopには利きません。margin-topの変更は履歴表示が歩かず以上になったら0固定となりますのでそれ以降は別手段によりアニメーションを行う必要があります。
今回は、scrollTopの設定をObservableにより1ずつ動かすことによって実現しています。ソースコードとしては、以下のような感じ。
var start = this.el.nativeElement.scrollTop;
var end = this.el.nativeElement.scrollHeight - this.el.nativeElement.clientHeight;
var cnt = 0;
var itr = 10;
var obs = Observable.interval(itr).subscribe((x) => {
cnt = cnt + 1;
this.el.nativeElement.scrollTop = start + cnt;
if((start+cnt) >= end){
obs.unsubscribe();
}
});
Observableにより所定の数値までintervalを動かし、所定の数値まできたらunsubscribeすると行った感じです。一様動きますが、ここも苦しいと感じる実装になっています。
まとめ
今回は、Component作成時に苦労した点を振り返りました。それにしてもチャットって去年くらいから凄い流行った印象ありますけど、実際にそれっぽいボトムアップ式のリストを作るとなるとUI周りで苦労しそうですね。
今回作った履歴表示部をそのまま応用しても、チャットだとインプット部分を可変でサイズ変えたりとか他にも考えないといけないこと多そうですし。また、別の機会に挑戦してみたいです。
目次
・始まり編(ionic、目的、作成したソースコードの公開)
・仕様編(アプリの仕様説明)
・Component編(ボトムアップ式のリスト実現方法、変更検知、謎のsetTimeout、アニメーション)
・Directive編(文字サイズの変更、変更検知、謎の計算式)
-----ここから下はいつか書く----
・Pipe編(カンマ)
・テスト編(環境構築、Providerのテスト)
・その他編(ion-iconって機種ごとにあったりなかったりで表示されないんですね)
・デプロイ周り編(platform直下のファイルについて、netlify、PWA)