Angular Materialで処理中の待ち受け画面
前回はAngularでRouter Outletに表示させた子コンポーネントでログイン処理中の画面を実装をしましたが、コードの省力化を考えてルートコンポーネントに持っていきます。
Router Outlet部に表示した子コンポーネントから親コンポーネントの変数を直接変更するのはルールに反していて、またイベントも伝える方法が(筆者には)見当たりません。
そこで、サービスとRxJSを使って実現してみました。
ここでは処理中画面の機能ですが、Router Outletの子コンポーネントから親コンポーネントにデータを渡したい場合にも応用ができると思います。
サービスの作成
まず処理中用のサービスのloadingServiceをつくり、Subjectオブジェクトを追加します。SubjectオブジェクトはObservableのobserverに相当し、nextを使って値を流すことができます。
loading.service.ts
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class LoadingService {
subject = new Subject<string>();
constructor() { }
setLoading(strMessage: string): void {
this.subject.next(strMessage);
}
}
ルートコンポーネントの修正(スクリプト)
おそらく、ルートコンポーネントではngOnInitをimplementsしていないので、その記述を加えた上で、ngOnInitを設定します。
次にルートコンポーネントにLoadingServiceを設定し、Observableオブジェクトを定義します。これはサービスにあるsubjectから生成しています。
Observableオブジェクトは内部にobserverを持ち、初期化時の関数でその挙動を事前に設定するものでした。subjectオブジェクトの場合はobserverを直接操作するのに加え、それに相当するObservableオブジェクトも同時に持つものです。
つまりsubjectでnextを使ってデータを渡すと、asObservableで返るObservableオブジェクトにデータを流すことになります。
app.component.ts
import { Component, OnInit } from '@angular/core';
...
import { Observable } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { LoadingService } from './loading.service';
...
loading$: Observable<string> = this.loadingService.subject.asObservable();
strMsg:string="";
export class AppComponent implements OnInit{
...
constructor(private loadingService: LoadingService) { }
...
ngOnInit(): void {
...
this.loading$.subscribe(
(value: string) => { this.strMsg=value; }
);
...
}
}
...
ルートコンポーネントの修正(HTML)
待ち受けの画面の部分を記述します。HTMLではstrMsgに文字が入っているときに表示するように設定します。mat-spinnerはAngular Materialで使える処理中画像です。
app.component.html
...
<div *ngIf="strMsg!==''" class="loading">
<div class="loading-inner">
<mat-spinner></mat-spinner>
{{strMsg}}
</div>
</div>
...
ルートコンポーネントの修正(CSS)
処理中の画面はCSSでposition: fixedを使って通常の画面から外して画面全体に表示させます。
app.component.css
...
.loading {
display: table;
width: 100%;
height: 100%;
position: fixed;
top: 0;
left: 0;
background-color: #fff;
opacity: 0.8;
z-index: 1;
}
.loading-inner {
display: table-cell;
text-align: center;
vertical-align: middle;
}
mat-spinner {
margin: 0 auto;
}
...
子コンポーネントから設定
子コンポーネントでは、Loadingサービスを設定しサービスのsetLoadingに文字列をセットすることで表示、ブランクをセットすることで消去することができます。
any.compornent.ts
...
constructor(private loadingService: LoadingService) { }
...
showLoading(strMessage: string):void {
//処理中の画面を表示させます
this.loadingService.setLoading(strMessage);
}
hideLoading():void {
//処理中の画面を消します
this.loadingService.setLoading("");
}
...