AngularMaterialアプリケーションを作成してデプロイする方法
公開: 2022-03-10Angularは、新しいWebアプリケーションを作成する際の人気のある選択肢の1つです。 さらに、「マテリアルデザイン」の仕様は、今日、最小限の魅力的なエクスペリエンスを作成するための頼りになる選択肢になっています。 したがって、新しい「Angular」プロジェクトでは、ほとんどの場合、「Angular Material Design Library」を使用して、マテリアルデザインの仕様に準拠したコンポーネントを使用します。 スムーズなアニメーションから適切なインタラクションフィードバックまで、これらすべてがAngularの公式マテリアルデザインライブラリの一部としてすでに利用可能です。
Webアプリケーションが開発されたら、次のステップはそれをデプロイすることです。 そこで、「Netlify」が登場します。 非常に使いやすいインターフェース、自動展開、A / Bテスト用のトラフィック分割、その他のさまざまな機能を備えたNetlifyは、確かに優れたツールです。
この記事は、公式のAngular MaterialDesignライブラリを使用してAngular8Webアプリケーションを作成するためのウォークスルーです。 Netlifyでホストされている間、Angularに完全に基づいたQRコードジェネレーターWebアプリケーションを作成します。
このチュートリアルのファイルはGitHubにあり、デモバージョンはここにデプロイされています。
入門
- Angular8をインストールします
- GitHubアカウントを作成し、
- コンピューターにGitをインストールします。
- Netlifyアカウントを作成します。
注:優先IDEおよびOSとしてVSCodeとMicrosoft Windowsを使用しますが、手順は他のOS上の他のIDEでも同様です。
上記の前提条件が完了したら、始めましょう!
モックと計画
プロジェクトの作成を開始する前に、事前に計画を立てておくと便利です。アプリケーションにどのようなUIが必要ですか。 再利用可能なものはありますか? アプリケーションは外部サービスとどのように相互作用しますか?
まず、UIモックを確認します。
これらは、アプリケーションに含まれる3つの異なるページです。 ホームページがアプリケーションの出発点になります。 QRページの作成は、新しいQRコードの作成を処理する必要があります。 履歴ページには、保存されているすべてのQRコードが表示されます。
モックアップは、アプリケーションのルックアンドフィールのアイデアを提供するだけでなく、各ページの責任を分離します。
(モックからの)1つの観察は、上部のナビゲーションバーがすべてのページに共通しているように見えることです。 したがって、ナビゲーションバーは再利用可能なコンポーネントとして作成し、再利用できます。
アプリケーションがどのように表示され、何を再利用できるかについて、かなりのアイデアが得られたので、始めましょう。
新しいAngularプロジェクトの作成
VSCodeを起動し、VSCodeでターミナルウィンドウを開いて、新しいAngularプロジェクトを生成します。
プロンプトに示されているように、ターミナルはデフォルトのパスで開きます。 続行する前に、優先ディレクトリに変更できます。 Windowsの場合は、 cd
コマンドを使用します。
今後、angular-cliには、 ng new <project-name>
を使用して新しいプロジェクトを生成するコマンドがあります。 好きなプロジェクト名を使用してEnterキーを押すだけです(例: ng new qr
)。
これにより、angular-cliマジックがトリガーされます。 プロジェクトのいくつかの側面を構成するためのいくつかのオプションを提供します。たとえば、角度ルーティングを追加します。 次に、選択したオプションに基づいて、変更なしで実行できるプロジェクトスケルトン全体が生成されます。
このチュートリアルでは、ルーティングに「はい」と入力し、スタイル設定に「 CSS 」を選択します。 これにより、新しいAngularプロジェクトが生成されます。
これで、完全に機能するAngularプロジェクトができました。 すべてが正しく機能していることを確認するために、ターミナルで次のコマンドを入力してプロジェクトを実行できます: ng serve
。 ええと、でも待ってください、これはエラーになります。 何が起こったのでしょうか?
心配しないでください。 angle-cliを使用して新しいプロジェクトを作成すると、コマンドng new qr
で指定されたプロジェクト名にちなんで名付けられたフォルダー内にスケルトン全体が生成されます。 ここでは、現在の作業ディレクトリを作成したばかりのディレクトリに変更する必要があります。 Windowsでは、コマンドcd qr
を使用してディレクトリを変更します。
ここで、 ng serve
を使用してプロジェクトを再実行してみてください。
Webブラウザーを開き、URL https:// localhost:4200にアクセスして、プロジェクトが実行されていることを確認します。 コマンドng serve
は、デフォルトでポート4200でアプリケーションを実行します。
ヒント:別のポートで実行するには、コマンドng serve --port <any-port>
を使用します。たとえば、 ng serve --port 3000
です。
これにより、基本的なAngularプロジェクトが確実に稼働します。 次へ移りましょう。
プロジェクトフォルダーをVSCodeに追加する必要があります。 「ファイル」メニューに移動し、「フォルダを開く」を選択して、プロジェクトフォルダを選択します。 プロジェクトフォルダが左側のエクスプローラービューに表示されます。
Angular MaterialLibraryの追加
Angularマテリアルライブラリをインストールするには、ターミナルウィンドウで次のコマンドを使用します: ng add @angular/material
。 これにより、(再び)必要なテーマ、デフォルトのアニメーションが必要かどうか、タッチサポートが必要かどうかなどの質問が表示されます。 デフォルトのIndigo/Pink
テーマを選択するだけで、 HammerJS
ライブラリとブラウザアニメーションを追加する場合は[ Yes
]を選択します。
上記のコマンドは、材料コンポーネントのサポートを有効にするようにプロジェクト全体を構成します。
- package.jsonにプロジェクトの依存関係を追加します。
- Robotoフォントをindex.htmlファイルに追加します。
- マテリアルデザインアイコンフォントをindex.htmlに追加します。
- また、いくつかのグローバルCSSスタイルを以下に追加します。
- 体から余白を取り除き、
-
height: 100%
、 - Robotoをデフォルトのアプリケーションフォントとして設定します。
すべてが正常であることを確認するために、この時点でプロジェクトを再度実行できますが、新しいことに気付くことはありません。
ホームページの追加
これで、プロジェクトスケルトンの準備が整いました。 ホームページを追加することから始めましょう。
上の写真のように、ホームページをシンプルに保ちたいと思います。 このホームページでは、いくつかの角度のあるマテリアルコンポーネントを使用しています。 分析しましょう。
- トップバーはシンプルなHTML
nav
要素で、マテリアルスタイルのボタンであるmat-button
が含まれ、その子として画像とテキストが含まれています。 バーの色は、Angularマテリアルライブラリを追加するときに選択した原色と同じです。 - 中央揃えの画像。
- もう1つの
mat-button
で、子としてテキストだけが表示されます。 このボタンを使用すると、ユーザーは履歴ページに移動できます。 - 上記のボタンに付けられたカウントバッジ
matBadge
は、ユーザーが保存したQRコードの数を示します。 - 選択したテーマのアクセントカラーが付いた、右下隅にあるフローティングアクションボタン
mat-fab
。
少し離れて、最初に他の必要なコンポーネントとサービスを追加しましょう。
ヘッダーの追加
以前に計画したように、ナビゲーションバーは再利用する必要があります。別の角度コンポーネントとして作成しましょう。 VSCodeでターミナルを開き、 ng gc header
(ng generate component headerの略)と入力してEnterキーを押します。 これにより、「header」という名前の新しいフォルダが作成されます。このフォルダには、次の4つのファイルが含まれます。
- header.component.css :このコンポーネントのスタイルを提供するために使用されます。
- header.component.html :HTML要素を追加するため。
- header.component.spec.ts :テストケースを作成するため。
- header.component.ts :Typescriptベースのロジックを追加します。
ヘッダーをモックのように見せるために、 header.component.htmlに以下のHTMLを追加します。
<nav class="navbar" [class.mat-elevation-z8]=true> <div> <button *ngIf="showBackButton" aria-hidden=false mat-icon-button routerLink="/"> <mat-icon> <i class="material-icons md-32">arrow_back</i> </mat-icon> </button> <span>{{currentTitle}}</span> </div> <button *ngIf="!showBackButton" aria-hidden=false mat-button class="button"> <img src="../../assets/qr-icon-white.png"> <span>QR Generator</span> </button> <button *ngIf="showHistoryNav" aria-hidden=false mat-button class="button" routerLink="/history"> <span>History</span> </button> </nav>
ヒント:任意のマテリアルコンポーネントに標高を追加するには、 [class.mat-elevation-z8]=true
を使用します。標高値は、 z値(この場合はz8
)を変更することで変更できます。 たとえば、標高を16に変更するには、 [class.mat-elevation-z16]=true
使用します。
上記のHTMLスニペットでは、 mat-icon
とmat-button/mat-icon-button
2つのAngularマテリアル要素が使用されています。 それらの使用法は非常に簡単です。 まず、以下に示すように、これら2つをapp.module.tsのモジュールとして追加する必要があります。
これにより、これら2つのAngularマテリアル要素を任意のコンポーネントのどこでも使用できるようになります。
マテリアルボタンを追加するには、次のHTMLスニペットが使用されます。
<button mat-button> Material Button </button>
Angularマテリアルライブラリには、 mat-raised-button
、 mat-flat-button
、 mat-fab
など、さまざまなタイプのマテリアルボタン要素があります。 上記のコードスニペットのmat-button
を他のタイプに置き換えるだけです。
もう1つの要素は、マテリアルアイコンライブラリで使用可能なアイコンを表示するために使用されるmat-icon
です。 Angularマテリアルライブラリが最初に追加されたときに、マテリアルアイコンライブラリへの参照も追加されました。これにより、膨大な数のアイコンのアイコンを使用できるようになりました。
使用法は次のように簡単です。
<mat-icon> <i class="material-icons md-32">arrow_back</i> </mat-icon>
ネストされた<i>
タグを使用して、アイコンのサイズ(ここではmd-32
)を変更できます。これにより、アイコンの高さと幅が32ピクセルになります。 この値は、 md-24
、 md-48
などになります。 ネストされた<i>
タグの値は、アイコンの名前です。 (他のアイコンの名前はここにあります。)
アクセシビリティ
アイコンや画像を使用する場合は常に、アクセシビリティの目的やスクリーンリーダーのユーザーに十分な情報を提供することが不可欠です。 ARIA(Accessible Rich Internet Applications)は、障害を持つ人々がWebコンテンツとWebアプリケーションにアクセスしやすくする方法を定義しています。
注意すべき点の1つは、ネイティブセマンティクス( nav
など)を持つHTML要素はARIA属性を必要としないということです。 スクリーンリーダーは、 nav
がナビゲーション要素であることをすでに認識しており、そのように読みます。
ARIAの仕様は、役割、状態、プロパティの3つのカテゴリに分けられます。 HTMLコードでプログレスバーを作成するためにdiv
が使用されているとしましょう。 ネイティブセマンティクスはありません。 ARIAロールは、このウィジェットをプログレスバーとして記述でき、ARIAプロパティは、ドラッグできるなどの特性を示すことができます。 ARIA状態は、プログレスバーの現在の値などの現在の状態を記述します。 以下のスニペットを参照してください。
<div role="progressbar" aria-valuenow="75" aria-valuemin="0" aria-valuemax="100"> </div>
同様に、非常に一般的に使用されるaria属性: aria-hidden=true/false
が使用されます。 値trueを指定すると、その要素はスクリーンリーダーから見えなくなります。
このアプリケーションで使用されるUI要素のほとんどはネイティブのセマンティックな意味を持っているため、使用されるARIA属性は、ARIAの可視性の状態を指定することだけです。詳細については、これを参照してください。
header.component.htmlには、現在のページに応じて戻るボタンを表示および非表示にするロジックが含まれています。 さらに、ホームボタンには、 /assets
フォルダーに追加する必要のある画像/ロゴも含まれています。 ここから画像をダウンロードして、 /assets
フォルダーに保存します。
ナビゲーションバーのスタイルを設定するには、 header.component.cssに以下のcssを追加します。
.navbar { position: fixed; top: 0; left: 0; right: 0; z-index: 2; background: #3f51b5; display: flex; flex-wrap: wrap; align-items: center; padding: 12px 16px; } .button { color: white; margin: 0px 10px; }
ヘッダーコンポーネントを他のコンポーネント間で再利用できるようにしたいので、何を表示するかを決定するには、他のコンポーネントからのパラメーターとしてそれらを必要とします。 これには、 header.component.htmlで使用した変数にバインドする@Input()
デコレータを使用する必要があります。
これらの行をheader.component.tsファイルに追加します。
// Add these three lines above the constructor entry. @Input() showBackButton: boolean; @Input() currentTitle: string; @Input() showHistoryNav: boolean; constructor() { }
上記の3つのバインディングは、ヘッダーコンポーネントが使用する他のコンポーネントからパラメーターとして渡されます。 前進すれば、その使用法はより明確になります。
次に、Angularコンポーネントで表現できるホームページを作成する必要があります。 それでは、別のコンポーネントを作成することから始めましょう。 ターミナルにng gc home
と入力して、ホームコンポーネントを自動生成します。 以前と同様に、「home」という名前の新しいフォルダが作成され、4つの異なるファイルが含まれます。 これらのファイルの変更に進む前に、Angularルーティングモジュールにルーティング情報を追加しましょう。
ルーティングの追加
Angularは、URLを特定のコンポーネントにマップする方法を提供します。 ナビゲーションが発生するたびに、AngularフレームワークはURLを監視し、 app-routing.module.tsファイルに存在する情報に基づいています。 マップされたコンポーネントを初期化します。 このように、さまざまなコンポーネントが他のコンポーネントを初期化する責任を負う必要はありません。 この場合、アプリケーションには、さまざまなボタンをクリックしてナビゲートできる3つのページがあります。 これは、Angularフレームワークによって提供されるルーティングサポートを活用することで実現します。
ホームコンポーネントは、アプリケーションの開始点である必要があります。 この情報をapp-routing.module.tsファイルに追加しましょう。
path
プロパティは空の文字列として設定されます。 これにより、アプリケーションのURLをホームページコンポーネント(Googleホームページを表示するgoogle.com
など)にマッピングできます。
ヒント:パスの値が「 /
」で始まることはありませんが、パスがsearch/coffee
のようになっている場合でも、代わりに空の文字列を使用します。
ホームページコンポーネントに戻り、 home.component.htmlのコンテンツを次のように置き換えます。
<app-header [showBackButton]="false" [currentTitle]=""></app-header> <app-profile></app-profile> <!-- FAB Fixed --> <button mat-fab class="fab-bottom-right" routerLink="/create"> <mat-icon> <i class="material-icons md-48">add</i> </mat-icon> </button>
ホームコンポーネントには3つの部分があります。
- 再利用可能なヘッダーコンポーネント
<app-header>
、 - プロファイルコンポーネント
<app-profile>
、 - 右下のフローティングアクションボタン。
上記のHTMLスニペットは、再利用可能なヘッダーコンポーネントが他のコンポーネントでどのように使用されるかを示しています。 コンポーネントセレクターを使用して、必要なパラメーターを渡すだけです。
プロファイルコンポーネントは、ホームページの本文として使用するために作成されます。まもなく作成されます。
+
アイコンが付いたフローティングアクションボタンは、画面の右下にあるmat-fab
タイプのAngularマテリアルボタンの一種です。 app-routing.module.ts
で提供されるルート情報をナビゲーションに使用するrouterLink
属性ディレクティブがあります。 この場合、ボタンのルート値は/ createとして設定され、コンポーネントを作成するためにマップされます。
作成ボタンを右下にフロートさせるには、 home.component.cssに以下のCSSコードを追加します。
.fab-bottom-right { position: fixed; left: auto; bottom: 5%; right: 10%; }
プロファイルコンポーネントはホームページの本文を管理することになっているため、 home.component.ts
はそのままにしておきます。
プロファイルコンポーネントの追加
ターミナルを開き、 ng gc profile
と入力し、Enterキーを押してプロファイルコンポーネントを生成します。 以前に計画したように、このコンポーネントはホームページの本体を処理します。 profile.component.html
を開き、そのコンテンツを次のように置き換えます。
<div class="center profile-child"> <img class="avatar" src="../../assets/avatar.png"> <div class="profile-actions"> <button mat-raised-button matBadge="{{historyCount}}" matBadgeOverlap="true" matBadgeSize="medium" matBadgeColor="accent" color="primary" routerLink="/history"> <span>History</span> </button> </div> </div>
上記のHTMLスニペットは、マテリアルライブラリのmatBadge
要素の使用方法を示しています。 ここで使用できるようにするには、 MatBadgeModule
をapp.module.ts
ファイルに追加するという通常のドリルに従う必要があります。 バッジは、ボタン、アイコン、テキストなどのUI要素の小さな絵のステータス記述子です。 この場合、ユーザーが保存したQRの数を表示するボタンとともに使用されます。 Angularマテリアルライブラリバッジには、 matBadgeSize
でバッジの位置を設定する、サイズを指定するmatBadgePosition
、バッジの色を設定するmatBadgeColor
など、他のさまざまなプロパティがあります。
もう1つの画像アセットをアセットフォルダに追加する必要があります:ダウンロード。 同じものをプロジェクトの/assets
フォルダーに保存します。
profile.component.cssを開き、これを追加します。
.center { top: 50%; left: 50%; position: absolute; transform: translate(-50%, -50%); } .profile-child { display: flex; flex-direction: column; align-items: center; } .profile-actions { padding-top: 20px; } .avatar { border-radius: 50%; width: 180px; height: 180px; }
上記のCSSは、計画どおりにUIを実現します。
次に、以前に使用したmatBadge
に反映されるため、履歴カウント値を更新するための何らかのロジックが必要です。 profile.component.tsを開き、次のスニペットを適切に追加します。
export class ProfileComponent implements OnInit { historyCount = 0; constructor(private storageUtilService: StorageutilService) { } ngOnInit() { this.updateHistoryCount(); } updateHistoryCount() { this.historyCount = this.storageUtilService.getHistoryCount(); } }
StorageutilServiceを追加しましたが、これまでそのようなサービスを作成していません。 エラーを無視して、プロファイルコンポーネントが完成し、ホームページコンポーネントも完成しました。 ストレージユーティリティサービスを作成した後、このプロファイルコンポーネントに再度アクセスします。 では、そうしましょう。
ローカルストレージ
HTML5は、データをローカルに保存するために使用できるWebストレージ機能を提供します。 これにより、Cookieと比較してはるかに多くのストレージが提供されます(少なくとも5MB対4KB)。 スコープと有効期間が異なる2種類のWebストレージがあります。ローカルとセッションです。 前者はデータを永続的に保存できますが、後者は一時的で単一のセッション用です。 タイプを選択する決定はユースケースに基づいて行うことができます。このシナリオでは、セッション間で保存したいので、ローカルストレージを使用します。
各データは、キーと値のペアで保存されます。 QRが生成されたテキストをキーとして使用し、base64文字列としてエンコードされたQR画像を値として使用します。 エンティティフォルダーを作成し、フォルダー内に新しいqr-object.tsファイルを作成し、次のようにコードスニペットを追加します。
クラスの内容:
export class QR { text: string; imageBase64: string; constructor(text: string, imageBase64: string) { this.imageBase64 = imageBase64; this.text = text; } }
ユーザーが生成されたQRを保存するたびに、上記のクラスのオブジェクトを作成し、ストレージユーティリティサービスを使用してそのオブジェクトを保存します。
新しいサービスフォルダを作成します。多くのサービスを作成します。それらをグループ化することをお勧めします。
現在の作業ディレクトリをservices、 cd services
に変更し、 ng gs <any name>
を使用して新しいサービスを作成します。 これは、 ng generate service <any name>
ための省略形ng gs storageutil
と入力し、Enterキーを押します。
これにより、2つのファイルが作成されます。
- storageutil.service.ts
- storageutil.service.spec.ts
後者は単体テストを書くためのものです。 storageutil.service.tsを開き、これを追加します。
private historyCount: number; constructor() { } saveHistory(key : string, item :string) { localStorage.setItem(key, item) this.historyCount = this.historyCount + 1; } readHistory(key : string) : string { return localStorage.getItem(key) } readAllHistory() : Array<QR> { const qrList = new Array<QR>(); for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); const value = localStorage.getItem(key); if (key && value) { const qr = new QR(key, value); qrList.push(qr); } } this.historyCount = qrList.length; return qrList; } getHistoryCount(): number { if (this.historyCount) { return this.historyCount; } this.readAllHistory(); return this.historyCount; } deleteHistory(key : string) { localStorage.removeItem(key) this.historyCount = this.historyCount - 1; }
qr-objectクラスをインポートして、エラーを修正します。 ローカルストレージ機能を使用するために、新しいものをインポートする必要はありません。キーワードlocalStorage
を使用して、キーに基づいて値を保存または取得するだけです。
次に、 profile.component.tsファイルを再度開き、 StorageutilService
クラスをインポートして、プロファイルコンポーネントを適切に終了します。
プロジェクトを実行すると、ホームページが計画どおりに稼働していることがわかります。
QRページの作成を追加する
ホームページの準備はできていますが、作成/追加ボタンは何もしません。 心配しないでください、実際のロジックはすでに書かれています。 routerLink
ディレクティブを使用してURLのベースパスを/create
に変更しましたが、 app-routing.module.tsファイルにマッピングが追加されていませんでした。
新しいQRコードの作成を処理するコンポーネントを作成し、 ng gc create-qr
と入力し、Enterキーを押して新しいコンポーネントを生成しましょう。
app-routing.module.tsファイルを開き、以下のエントリをroutes
配列に追加します。
{ path: 'create', component: CreateQrComponent },
これにより、 CreateQRComponent
がURL /create
にマップされます。
create-qr.components.htmlを開き、内容を次のように置き換えます。
<app-header [showBackButton]="showBackButton" [currentTitle]="title" [showHistoryNav]="showHistoryNav"></app-header> <mat-card class="qrCard" [class.mat-elevation-z12]=true> <div class="qrContent"> <!--Close button section--> <div class="closeBtn"> <button mat-icon-button color="accent" routerLink="/" matTooltip="Close"> <mat-icon> <i class="material-icons md-48">close</i> </mat-icon> </button> </div> <!--QR code image section--> <div class="qrImgDiv"> <img *ngIf="!showProgressSpinner" src={{qrCodeImage}} width="200px" height="200px"> <mat-spinner *ngIf="showProgressSpinner"></mat-spinner> <div class="actionButtons" *ngIf="!showProgressSpinner"> <button mat-icon-button color="accent" matTooltip="Share this QR"> <mat-icon> <i class="material-icons md-48">share</i> </mat-icon> </button> <button mat-icon-button color="accent" (click)="saveQR()" matTooltip="Save this QR"> <mat-icon> <i class="material-icons md-48">save</i> </mat-icon> </button> </div> </div> <!--Textarea to write any text or link--> <div class="qrTextAreaDiv"> <mat-form-field> <textarea matInput [(ngModel)]="qrText" cdkTextareaAutosize cdkAutosizeMinRows="4" cdkAutosizeMaxRows="4" placeholder="Enter a website link or any text..."></textarea> </mat-form-field> </div> <!--Create Button--> <div class="createBtnDiv"> <button class="createBtn" mat-raised-button color="accent" matTooltip="Create new QR code" matTooltipPosition="above" (click)="createQrCode()">Create</button> </div> </div> </mat-card>
上記のスニペットは、Angularマテリアルライブラリ要素の多くを使用しています。 計画どおり、必要なパラメーターが渡されるヘッダーコンポーネント参照が1つあります。 次は、作成ページの本体です。 [class.mat-elevation-z12]=true
が使用されているため、1枚のAngularマテリアルカードまたはmat-card
カードで構成され、中央に配置され、最大12pxまで持ち上げられます。
マテリアルカードは、他のdiv
タグとして使用できる別の種類のコンテナです。 マテリアルライブラリは、以下に示すように、画像の配置、タイトル、サブタイトル、説明、アクションなど、明確に定義された情報をmat-card
に配置するためのいくつかのプロパティを提供します。
上記のHTMLスニペットでは、他のコンテナと同じようにmat-card
を使用しています。 使用される別のマテリアルライブラリ要素はmatTooltip
です。 これは使いやすさを備えたもう1つのツールチップであり、ユーザーが要素にカーソルを合わせたり長押ししたりしたときに表示されます。 以下のスニペットを使用して、ツールチップを表示してください。
matTooltip="Any text you want to show"
アイコンボタンやその他のUI要素とともに使用して、追加情報を伝えることができます。 アプリケーションのコンテキストでは、アイコンを閉じるボタンに関する情報が表示されます。 ツールチップの配置を変更するには、 matTooltipPosition
を使用します。
matTooltip="Any text you want to show" matTooltipPosition="above"
matTooltip
の他に、 mat-spinner
spinnerを使用して読み込みの進行状況を表示します。 ユーザーが「作成」ボタンをクリックすると、ネットワーク呼び出しが行われます。 これは、進行状況スピナーが表示されるときです。 ネットワーク呼び出しが結果とともに返されるとき、スピナーを非表示にするだけです。 これは、次のように簡単に使用できます。
<mat-spinner *ngIf="showProgressSpinner"></mat-spinner>
showProgressSpinner
は、進行状況スピナーを表示/非表示にするために使用されるブール変数です。 ライブラリには、色を変更するため[color]='accent'
、進行状況スピナーのタイプを変更するため[mode]='indeterminate'
などの他のパラメーターも用意されています。 不確定な進行状況スピナーはタスクの進行状況を表示しませんが、確定的な進行状況スピナーはタスクの進行状況を反映するために異なる値を持つことができます。 ここでは、ネットワークコールにかかる時間がわからないため、不確定なスピナーが使用されています。
マテリアルライブラリは、マテリアルガイドラインに準拠したtextareaのバリアントを提供しますが、 mat-form-field
の子孫としてのみ使用できます。 マテリアルtextareaの使用法は、以下のように、デフォルトのHTMLと同じくらい簡単です。
<mat-form-field> <textarea matInput placeholder="Hint text"></textarea> </mat-form-field>
matInput
は、ネイティブinput
タグがmat-form-field
で機能できるようにするディレクティブです。 placeholder
プロパティを使用すると、ユーザーのヒントテキストを追加できます。
ヒント: cdkTextareaAutosize
プロパティを使用して、自動サイズ変更可能にします。 cdkAutosizeMinRows
とcdkAutosizeMaxRows
を使用して行と列を設定し、3つすべてを一緒に使用して、設定された最大の行と列の制限に達するまでtextareaの自動サイズ変更を行います。
これらすべてのマテリアルライブラリ要素を使用するには、それらをapp.module.tsファイルに追加する必要があります。
HTMLで使用されているプレースホルダー画像があります。 ダウンロードして/assets
フォルダーに保存します。
上記のHTMLにもCSSスタイルが必要なので、 create-qr.component.tsファイルを開いて以下を追加します。
.qrCard { display: flex; flex-direction: column; align-items: center; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 20%; height: 65%; padding: 50px 20px; } .qrContent { display: flex; flex-direction: column; align-items: center; width: 100%; } .qrTextAreaDiv { width: 100%; display: flex; flex-direction: row; justify-content: center; padding: 0px 0px; position: absolute; bottom: 10%; } .createBtn { left: 50%; transform: translate(-50%, 0px); width: 80%; } .createBtnDiv { position: absolute; bottom: 5%; width: 100%; } .closeBtn { display: flex; flex-direction: row-reverse; align-items: flex-end; width: 100%; margin-bottom: 20px; } .closeBtnFont { font-size: 32px; color: rgba(0,0,0,0.75); } .qrImgDiv { top: 20%; position: absolute; display: flex; flex-direction: column; align-items: center; justify-content: center; width: 100%; } .actionButtons { display: flex; flex-direction: row; padding-top: 20px; }
UIをロジックに接続しましょう。 create-qr.component.tsファイルを開き、以下のコードを追加して、すでに存在する行を残します。
export class CreateQrComponent implements OnInit { qrCodeImage = '../../../assets/download.png'; showProgressSpinner = false; qrText: string; currentQR; showBackButton = true; title = 'Generate New QR Code'; showHistoryNav = true; constructor(private snackBar: MatSnackBar, private restutil: RestutilService, private storageService: StorageutilService) { } ngOnInit() { } createQrCode() { //Check if any value is given for the qr code text if (!!this.qrText) { //Make the http call to load qr code this.loadQRCodeImage(this.qrText); } else { //Show snackbar this.showSnackbar('Enter some text first') } } public loadQRCodeImage(text: string) { // Show progress spinner as the request is being made this.showProgressSpinner = true; // Trigger the API call this.restutil.getQRCode(text).subscribe(image =>{ // Received the result - as an image blob - require parsing this.createImageBlob(image); }, error => { console.log('Cannot fetch QR code from the url', error) // Hide the spinner - show a proper error message this.showProgressSpinner = false; }); } private createImageBlob(image: Blob) { // Create a file reader to read the image blob const reader = new FileReader(); // Add event listener for "load" - invoked once the blob reading is complete reader.addEventListener('load', () => { this.qrCodeImage = reader.result.toString(); //Hide the progress spinner this.showProgressSpinner = false; this.currentQR = reader.result.toString(); }, false); // Read image blob if it is not null or undefined if (image) { reader.readAsDataURL(image); } } saveQR() { if (!!this.qrText) { this.storageService.saveHistory(this.qrText, this.currentQR); this.showSnackbar('QR saved') } else { //Show snackbar this.showSnackbar('Enter some text first') } } showSnackbar(msg: string) { //Show snackbar this.snackBar.open(msg, '', { duration: 2000, }); } }
ユーザーにコンテキスト情報を提供するために、マテリアルデザインライブラリのMatSnackBar
も使用しています。 これは画面の下からポップアップとして表示され、消える前に数秒間留まります。 これは要素ではなく、Typescriptコードから呼び出すことができるサービスです。
メソッド名showSnackbar
の上記のスニペットは、スナックバーを開く方法を示していますが、使用する前に、他のマテリアルライブラリ要素の場合と同じように、 app.module.tsファイルにMatSnackBar
エントリを追加する必要があります。
ヒント:最近のAngularマテリアルライブラリバージョンでは、スナックバーのスタイルを簡単に変更する方法はありません。 代わりに、コードに2つの追加を行う必要があります。
まず、以下のCSSを使用して、背景色と前景色を変更します。
::ng-deep snack-bar-container.snackbarColor { background-color: rgba(63, 81, 181, 1); } ::ng-deep .snackbarColor .mat-simple-snackbar { color: white; }
次に、 panelClass
というプロパティを使用して、スタイルを上記のCSSクラスに設定します。
this.snackBar.open(msg, '', { duration: 2000, panelClass: ['snackbarColor'] });
上記の2つの組み合わせにより、マテリアルデザインライブラリのスナックバーコンポーネントにカスタムスタイルを設定できます。
これでQRページの作成手順は完了ですが、まだ1つ欠けています。 create-qr.component.tsファイルを確認すると、欠落している部分に関するエラーが表示されます。 このパズルに欠けているのは、サードパーティのAPIからQRコード画像を取得するRestutilService
です。
ターミナルで、 ng gs restutil
と入力し、Enterキーを押して、現在のディレクトリをサービスに変更します。 これにより、RestUtilServiceファイルが作成されます。 restutil.service.tsファイルを開き、次のスニペットを追加します。
private edgeSize = '300'; private BASE_URL = 'https://api.qrserver.com/v1/create-qr-code/?data={data}!&size={edge}x{edge}'; constructor(private httpClient: HttpClient) { } public getQRCode(text: string): Observable { // Create the url with the provided data and other options let url = this.BASE_URL; url = url.replace("{data}", text).replace(/{edge}/g, this.edgeSize); // Make the http api call to the url return this.httpClient.get(url, { responseType: 'blob' }); }
private edgeSize = '300'; private BASE_URL = 'https://api.qrserver.com/v1/create-qr-code/?data={data}!&size={edge}x{edge}'; constructor(private httpClient: HttpClient) { } public getQRCode(text: string): Observable { // Create the url with the provided data and other options let url = this.BASE_URL; url = url.replace("{data}", text).replace(/{edge}/g, this.edgeSize); // Make the http api call to the url return this.httpClient.get(url, { responseType: 'blob' }); }
上記のサービスはサードパーティのAPIからQR画像をフェッチしますが、応答はJSONタイプではなく画像であるため、上記のスニペットではresponseType
を'blob'
として指定します。
Angularは、HTTPサポートサーバーと通信するためのHttpClient
クラスを提供します。 起動前のリクエストのフィルタリング、レスポンスの取得、コールバックなどによるレスポンスの処理の有効化など、多くの機能を提供します。 同じものを使用するには、 app.module.tsファイルにHttpClientModuleのエントリを追加します。
最後に、このサービスをcreate-qr.component.tsファイルにインポートして、QRコードの作成を完了します。
ちょっと待って! 上記のQRロジックの作成に問題があります。 ユーザーが同じテキストを使用してQRを何度も生成すると、ネットワーク呼び出しが発生します。 これを修正する1つの方法は、リクエストベースをキャッシュすることです。これにより、リクエストテキストが同じ場合にキャッシュからの応答を提供します。
キャッシングリクエスト
Angularは、HTTP呼び出し、HttpClient、およびHttpInterceptorsを作成して、サーバーとの間でHTTP要求または応答を検査および変換するための簡略化された方法を提供します。 認証やキャッシングなどに使用でき、複数のインターセプターを追加してチェーン化してさらに処理することができます。 この場合、QRテキストが同じであれば、リクエストをインターセプトし、キャッシュからのレスポンスを提供します。
インターセプターフォルダーを作成してから、ファイルcache-interceptor.tsを作成します。
以下のコードスニペットをファイルに追加します。
import { Injectable } from '@angular/core'; import { HttpInterceptor, HttpResponse, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http'; import { tap } from 'rxjs/operators'; import { of, Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class RequestCachingService implements HttpInterceptor { private cacheMap = new Map<string, HttpResponse<any>>(); constructor() { } intercept(req: HttpRequest , next: HttpHandler): Observable<HttpEvent<any>> { const cachedResponse = this.cacheMap.get(req.urlWithParams); if (cachedResponse) { return of(cachedResponse); } return next.handle(req).pipe(tap(event => { if (event instanceof HttpResponse) { this.cacheMap.set(req.urlWithParams, event); } })) } }
import { Injectable } from '@angular/core'; import { HttpInterceptor, HttpResponse, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http'; import { tap } from 'rxjs/operators'; import { of, Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class RequestCachingService implements HttpInterceptor { private cacheMap = new Map<string, HttpResponse<any>>(); constructor() { } intercept(req: HttpRequest , next: HttpHandler): Observable<HttpEvent<any>> { const cachedResponse = this.cacheMap.get(req.urlWithParams); if (cachedResponse) { return of(cachedResponse); } return next.handle(req).pipe(tap(event => { if (event instanceof HttpResponse) { this.cacheMap.set(req.urlWithParams, event); } })) } }
上記のコードスニペットには、キーがリクエストURLであり、レスポンスが値であるマップがあります。 現在のURLがマップに存在するかどうかを確認します。 そうである場合は、応答を返します(残りは自動的に処理されます)。 URLがマップにない場合は、追加します。
まだ終わっていません。 app.module.tsへのエントリは、適切に機能するために必要です。 以下のスニペットを追加します。
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; import { CacheInterceptor } from './interceptor/cache-interceptor'; providers: [ { provide: HTTP_INTERCEPTORS, useClass: CacheInterceptor, multi: true } ],
This adds the caching feature to our application. Let's move on to the third page, the History page.
Adding The History Page
All the saved QR codes will be visible here. To create another component, open terminal type ng gc history
and press Enter.
Open history.component.css and add the below code:
.main-content { padding: 5% 10%; } .truncate { width: 90%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .center-img { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); display: flex; flex-direction: column; align-items: center; }
Open history.component.html and replace the content with this:
<app-header [showBackButton]="showBackButton" [currentTitle]="title" [showHistoryNav]="showHistoryNav"></app-header> <div class="main-content"> <mat-grid-list cols="4" rowHeight="500px" *ngIf="historyList.length > 0"> <mat-grid-tile *ngFor="let qr of historyList"> <mat-card> <img mat-card-image src="{{qr.imageBase64}}"> <mat-card-content> <div class="truncate"> {{qr.text}} </div> </mat-card-content> <mat-card-actions> <button mat-button (click)="share(qr.text)">SHARE</button> <button mat-button color="accent" (click)="delete(qr.text)">DELETE</button> </mat-card-actions> </mat-card> </mat-grid-tile> </mat-grid-list> <div class="center-img" *ngIf="historyList.length == 0"> <img src="../../assets/no-see.png" width="256" height="256"> <span>Nothing to see here</span> </div> </div>
As usual, we have the header component at the top. Then, the rest of the body is a grid list that will show all the saved QR codes as individual mat-card
. For the grid view, we are using mat-grid-list
from the Angular material library. As per the drill, before we can use it, we have to first add it to the app.module.ts file.
マットグリッドリストは、 mat-grid-tile
と呼ばれる複数のタイルの子を持つコンテナとして機能します。 上記のHTMLスニペットでは、各タイルは、他のUI要素の一般的な配置のためにそのプロパティの一部を使用してmat-card
を使用して作成されています。 number of columns
、幅を自動的に計算するために使用されるrowHeight
を指定できます。 上記のスニペットでは、列数とrowHeight
値の両方を提供しています。
履歴が空の場合はプレースホルダー画像を使用し、ダウンロードしてアセットフォルダーに追加します。
このすべての情報を取り込むためのロジックを実装するには、 history.component.tsファイルを開き、以下のスニペットをHistoryComponent
クラスに追加します。
showBackButton = true; title = 'History'; showHistoryNav = false; historyList; constructor(private storageService: StorageutilService, private snackbar: MatSnackBar ) { } ngOnInit() { this.populateHistory(); } private populateHistory() { this.historyList = this.storageService.readAllHistory(); } delete(text: string) { this.storageService.deleteHistory(text); this.populateHistory(); } share(text: string) { this.snackbar.open(text, '', {duration: 2000,}) }
上記のロジックは、保存されているすべてのQRをフェッチし、ページに入力するだけです。 ユーザーは保存されたQRを削除できます。これにより、ローカルストレージからエントリが削除されます。
これで履歴コンポーネントが終了します...それともそうですか? このコンポーネントのルートマッピングを追加する必要があります。 app-routing.module.tsを開き、履歴ページのマッピングも追加します。
{ path: 'history', component: HistoryComponent },
ルート配列全体は、今では次のようになっているはずです。
const routes: Routes = [ { path: '', component: HomeComponent }, { path: 'create', component: CreateQrComponent }, { path: 'history', component: HistoryComponent }, ];
今がアプリケーションを実行して完全なフローを確認する良い機会です。ターミナルを開いてng serve
と入力し、Enterキーを押します。 次に、 localhost:4200
に移動して、アプリケーションが機能していることを確認します。
GitHubに追加
デプロイ手順に進む前に、プロジェクトをGitHubリポジトリに追加するとよいでしょう。
- GitHubを開きます。
- 新しいリポジトリを作成します。
- VS Codeでは、ターミナルを使用し、クイックスタートガイドに記載されている最初のコマンドセットに従って、すべてのプロジェクトファイルをプッシュします。
ページを更新して、すべてのファイルが表示されているかどうかを確認してください。 この時点から、gitの変更(commit、pull / pushなど)は、この新しく作成されたリポジトリに反映されます。
Netlifyと展開
アプリケーションはローカルマシンで実行されますが、他のユーザーがアクセスできるようにするには、アプリケーションをクラウドプラットフォームにデプロイし、ドメイン名に登録する必要があります。 ここでNetlifyが活躍します。 継続的デプロイサービス、GitHubとの統合、その他多くのメリットを提供します。 今のところ、アプリケーションへのグローバルアクセスを有効にしたいと考えています。 始めましょう。
- Netlifyにサインアップします。
- ダッシュボードから、[ Gitから新しいサイト]ボタンをクリックします。
- 次の画面でGitHubをクリックします。
- NetlifyがGitHubリポジトリにアクセスできるようにすることを承認します。
- 新しく作成された
qr
リポジトリを検索して選択します。 - Netlifyでは、次のステップで、デプロイ用にGitHubリポジトリブランチを選択できます。 通常、
master
ブランチを使用しますが、リリース関連の安定した機能のみを含む別のrelease
ブランチを持つこともできます。
これはng build --prod
を追加します。 公開されたディレクトリは、 angular.json
ファイルに記載されているようにdist/qr
になります。
次に、[ Deploy site
]ボタンをクリックします。これにより、コマンドng build --prod
を使用してプロジェクトのビルドがトリガーされ、ファイルがdist/qr
に出力されます。
Netlifyにパス情報を提供したので、NetlifyはWebアプリケーションにサービスを提供するための正しいファイルを自動的に取得します。 Netlifyは、デフォルトでランダムドメインをアプリケーションに追加します。
これで、上のページにあるリンクをクリックして、どこからでもアプリケーションにアクセスできます。 最後に、アプリケーションがデプロイされました。
カスタムドメイン
上の画像では、サブドメインがランダムに生成されている間、アプリケーションのURLが示されています。 それを変えましょう。
[ Domain settings
]ボタンをクリックし、[カスタムドメイン]セクションで3ドットメニューをクリックして、[ Edit site name
]を選択します。
これにより、新しいサイト名を入力できるポップアップが開きます。 この名前は、Netlifyドメイン全体で一意である必要があります。 使用可能なサイト名を入力して、[保存]をクリックします。
これで、アプリケーションへのリンクが新しいサイト名で更新されます。
分割テスト
Netlifyが提供するもう1つの優れた機能は、分割テストです。 これにより、トラフィックの分割が可能になり、さまざまなユーザーセットがさまざまなアプリケーション展開と対話できるようになります。 新しい機能を別のブランチに追加して、トラフィックをこのブランチデプロイメントに分割し、トラフィックを分析してから、機能ブランチをメインデプロイメントブランチとマージすることができます。 設定しましょう。
分割テストを有効にするための前提条件は、少なくとも2つのブランチを持つGitHubリポジトリです。 以前に作成したGitHubのアプリリポジトリに移動し、新しいブランチを作成a
ます。
これで、リポジトリにmaster
ブランチとブランチがa
ます。 Netlifyはブランチ展開を行うように構成する必要があるため、Netlifyダッシュボードを開いてSettings
をクリックします。 左側で[ Build & Deploy
]、[ Continuous Deployment
]の順にクリックし、右側の[ Deploy contexts
]セクションで[ Edit settings
]をクリックします。
[ Branch deploys
]サブセクションで、[個々のブランチを追加する]オプションを選択し、ブランチ名を入力して保存します。
ブランスのデプロイは、Netlifyが提供するもう1つの便利な機能です。 デプロイするGitHubリポジトリブランチを選択できます。また、マージする前に、 master
ブランチへのすべてのプルリクエストのプレビューを有効にすることもできます。 これは、開発者がコードの変更をメインのデプロイメントブランチに追加する前に、実際に変更をライブでテストできる優れた機能です。
次に、ページ上部の[ Split Testing
]タブオプションをクリックします。 分割テストの構成をここに示します。
ブランチ(本番ブランチ以外)を選択できます—この場合a
。 トラフィックを分割する設定を試すこともできます。 各ブランチに割り当てられたトラフィックの割合に基づいて、Netlifyは一部のユーザーをブランチを使用しa
デプロイされたアプリケーションに再ルーティングし、他のユーザーをmaster
ブランチに再ルーティングします。 設定後、[ Start test
]ボタンをクリックして、トラフィック分割を有効にします。
ヒント: Netlifyは、接続されたGitHubリポジトリに複数のブランチがあることを認識せず、次のエラーが発生する可能性があります。
これを解決するには、 Build & Deploy
のオプションからリポジトリに再接続するだけです。
Netlifyは他にも多くの機能を提供します。 Netlifyのさまざまな側面を簡単に構成できることを示すために、その便利な機能のいくつかを紹介しました。
これで私たちは旅の終わりに到達します。 Webアプリケーションに基づいてAngularMaterialデザインを作成し、Netlifyにデプロイすることに成功しました。
結論
Angularは、Webアプリケーション開発のための優れた人気のあるフレームワークです。 公式のAngularマテリアルデザインライブラリを使用すると、ユーザーとの非常に自然なやり取りのために、マテリアルデザインの仕様に準拠したアプリケーションを簡単に作成できます。 さらに、優れたフレームワークで開発されたアプリケーションは、デプロイに優れたプラットフォームを使用する必要があり、Netlifyはまさにそれです。 絶え間ない進化、優れたサポート、および多数の機能を備えたこのプラットフォームは、Webアプリケーションや静的サイトを大衆に提供するための優れたプラットフォームであることは間違いありません。 うまくいけば、この記事が、考えから展開まで、新しいAngularプロジェクトを開始するのに役立つことを願っています。
参考文献
- Angularアーキテクチャ
- より角度のあるマテリアルコンポーネント
- Netlifyの機能の詳細