はじめに
Angular Material の Progress Spinner で進捗%をテキスト表示したのだが、意外とハマったので備忘録として作成。
やりたいことは以下の画像のようにしたいだけだが、テキスト表示を追加するにはコンポーネントを自作しないといけないっぽい。
デザインなどは別途修正するとして、とりあえず必要最低限のコードだけ抜粋。
コンポーネント
まずはコンポーネントを作成する。
html, ts, scssの3セット。
<mat-progress-spinner
mode="indeterminate"
diameter="100"
strokeWidth="8"
>
</mat-progress-spinner>
<div class="message">{{ message }}</div>
import { Component, ViewEncapsulation } from '@angular/core';
@Component({
selector: 'app-progress-spinner',
templateUrl: './progressSpinner.component.html',
styleUrls: [ './progressSpinner.component.scss' ],
encapsulation: ViewEncapsulation.None,
})
export class ProgressSpinnerComponent {
message: string;
constructor() {}
}
.progress-spinner {
color: white;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.progress-spinner-backdrop {
background-color: rgba(0, 0, 0, .30)!important;
}
.message {
font-size: x-large;
color: white;
position: absolute;
text-align: center;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
スピナーとテキストを持つだけのシンプルな構成。
スタイルシートはグローバルの styles.scss に書きたくなかったので切り出しているが、ViewEncapsulation.None
を付けないと正しく読み込めなかった。
(雑だが、主題とは関係ないので、とりあえず割愛。)
サービス
コンポーネントを生成、管理するためのサービスを追加。
overlay の attach、detach などもこちらで管理する。
overlay を生成する時に、panelClass、backdropClass に先ほどのスタイルシートで定義したクラスをセットしている。
import { ComponentRef } from '@angular/core';
import { Overlay, OverlayRef } from '@angular/cdk/overlay';
import { ComponentPortal } from '@angular/cdk/portal';
import { Injectable } from '@angular/core';
import { ProgressSpinnerComponent } from './progressSpinner.component';
@Injectable({
providedIn: 'root'
})
export class ProgressSpinnerService {
overlayRef: OverlayRef;
spinner: ComponentRef<ProgressSpinnerComponent>;
constructor(
private readonly overlay: Overlay,
) {}
// show progress spinner
public show(): void {
const portal = new ComponentPortal(ProgressSpinnerComponent);
this.overlayRef = this.overlay.create({
width: '100%',
height: '100%',
hasBackdrop: true,
panelClass: 'progress-spinner',
backdropClass: 'progress-spinner-backdrop',
});
this.spinner = this.overlayRef.attach(portal);
}
public close(): void {
this.overlayRef.detach();
this.overlayRef.dispose();
}
public setMessage(message): void {
this.spinner.instance.message = message;
}
}
app.module.ts
他から呼び出せるようにするために、MatProgressSpinnerModule、ProgressSpinnerComponent の定義を app.module.ts に追加する。
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { ProgressSpinnerComponent } from './views/common/components/progressSpinner/progressSpinner.component';
・
・
・
@NgModule({
declarations: [
AppComponent,
ProgressSpinnerComponent,
・
・
・
imports: [
・
・
・
MatProgressSpinnerModule,
呼び出し方
あとは任意のコンポーネントからサービスを import し、show → setMessage →・・・→ close としてやればOK。
import { ProgressSpinnerService } from '../../common/components/progressSpinner/progressSpinner.service';
・
・
・
constructor(
private spinner: ProgressSpinnerService,
) {}
・
・
・
test() {
try {
// show spinner
this.spinner.show();
this.spinner.setMessage('ファイルアップロード中 10%');
・
・
・
this.spinner.setMessage('ファイルアップロード中 20%');
・
・
・
} catch (e) {
alert(e.message);
} finally {
// stop spinner
this.spinner.close();
}
}
※ とりあえずスピナーは1つしかない前提。
複数ある場合、コンポーネントのインスタンスを返すようにするか、あるいはサービスを複数Injectするか。まあだいたい1つで事足りるでしょうけど。