Angular2を利用した開発を始め早3ヶ月。
速度感もそれなりで別に致命的な欠陥も特に見当たらずに来たのだけど、ルーティングをクライアントに任せる挙動に関して首を傾げることも多かった
例えば
画面遷移時に少なからず、
- 必要なCSS, JSの読み込み
- 画面描写処理
等がコンマ数秒のうちに行われるわけであるが、この間に何も描写されない時間が発生する。
しかもこのとき、ブラウザがデータをロードする必要が無いため全体的に完全に停止した様な状態になってしまう。
コンマ数秒なら良しとして、クライアント端末がヘビーな状態になっていた場合が問題である。
普通のWEBのサイトの場合クライアントが重かろうとテキストくらいは表示されるだろう
アニメーションを多用していようとアニメーションが止まる程度で済むだろう
Angularの場合「何も見えない」
loading中なのかすら判定できない
この仕組み自体はしかたがないので、せめてloading中にloading iconを表示させてやろうと言うのが今回の趣旨である。
このとき、
- 同ページ内で DerectiveのLoading
- 画面遷移によるComponentのLoading
どっちをくるくる回すかで処理をかえる必要がある(っぽい)
#1. 同ページ内で DerectiveのLoading
こっちならDerectiveに関数を渡してやって EventEmitterで拾って、Oinitとか、AtferViewInitとか、APIリクエストのCompletionコールバックとか、適当なところで親にloading開始と完了を伝えてやる。
export class MyComponent {
private _derectiveLoading = false;
startLoadingNewDerective() {
this._derectiveLoading = true;
// 子ディレクティブの追加とか
// directiveの初期化が発生する処理
// this.mydirective = new Obj();
}
derectiveStartLoading() {
this._derectiveLoading = true;
}
derectiveEndLoading() {
this._derectiveLoading = false;
}
}
<my_derective
(startLoading)="derectiveStartLoading()"
(endLoading)="derectiveEndLoading()"
></my_derective>
export class MyDerective OnInit, AfterViewInit {
public courses: Course[] = []
@Output() startLoading = new EventEmitter();
@Output() endLoading = new EventEmitter();
ngAfterViewInit(){
// this.startLoading.emit('event');
this.endLoading.emit('event');
}
※componentの初期化前に親Componentで loading=true
にしてやることも重要
Oninit ~ AfterViewInitまで処理してからloadingの書き換えが行われるので仕方ない。
ちなみに OninitとAtferViewInitの途中でloadingの値を変えると怒られる。描写に必要な変数がちょろちょろかわてると困るんだろう。
#2. 画面遷移によるComponentのLoading
こっちが問題。面倒だった。
画面遷移の場合、RouterOutlet というやつを使って依存関係が整理されている。
router-outlet タグを書いておくと urlの値から設定されたComponentをもってきてselector をrouter-outletの真下に置く
ぱっとみは router-outlet = 万能Selectorみたいな感じだけどそうじゃない
例えば下記のようなことはできない
<router-outlet
(startLoading)="derectiveStartLoading()"
(endLoading)="derectiveEndLoading()"
></router-outlet>
遷移後のComponentで変数を拾えない。ではどうやって変数を共有すればよいのか。
...ベストプラクティスとか見つからねぇw
そんなわけで安直に Service を使おう!
import {Injectable} from 'angular2/core';
@Injectable()
export class LoadingService {
constructor() { }
startLoading(){
setTimeout(()=>this.updateStatus(true), 0);
}
endLoading(){
setTimeout(()=>this.updateStatus(false), 0);
}
updateStatus(status){
if(loadingStatus !== status){
loadingStatus = status
}
}
isLoading(): boolean {
return loadingStatus;
}
}
var loadingStatus: boolean;
グローバル変数を操作することで、サービス呼び出し側で共通の変数を操作できる
また、見た目scopeがないのでグローバル変数と呼んでみたが、ファイルごとか何かでscopeがかかってるようにも見える。
TODO:真偽調査
スコープがかかってないようだったらシングルトンっぽく実装してもいいかも
そのうち元気が出たら動くサンプルコードを挙げてみたい