[Ionic入門] Angular/React/VueベースでWeb/iOS/Androidを同時開発可能なクロスプラットフォームフレームワーク

  • クロスプラットフォームで動作可能なアプリを開発できるIonic Frameworkの概要と初期開発手順についてのメモ
  • モダンフロントエンドフレームワーク(ex. Angular,React,Vue)ベースで開発したAPをモバイル(iOS/Android)に変換することができる
    • Web系のフロントエンドエンジニアが覚えると、ほぼ学習コスト無しで全てに対応可能になる
  • Flutterとどちらを採用すべきか?は開発チームのエンジニアによる
    • WEB技術(Typescript)が軸→Ionic、0からDartを学びたい→Flutter

Ionic Framework概要

クロスアプリケーションフレームワーク

Ionic概要

  • SPA(Webアプリ)ベースのアプリをiOS/Androidで動くネイティブアプリに変換できるフレームワーク

    • Web/iOS/Androidのアプリをハイブリット展開可能
      • 1つのIonic Projectから Web/iOS/AndroidのAPをビルドするイメージ
    • 特に、既存のWEB APのモバイル化に迅速に対応できる
  • コンポーネントやコード(HTML/Scss/Typescript)を使い回して開発可能

    • Typescriptで書いたロジックをそのまま使い回せる
    • Swift等を使うと、Webとモバイルで同じロジックを別言語で書き直す手間が発生する
  • WEBフレームワークとはUI開発で一部差分がある

    • 例えば、Angularで使われるWeb専用のUIコンポーネントは使用できない

      • 一般的にAngularではAngular Materialを活用して、htmlやcssをあまり書かずにUIを実装していく
        • ヘッダー、メニュー、テーブル等の部品がある
          Angular Material
    • Ionicではモバイル向けのUIコンポーネントを活用する
      Ionic UI Component

      • IonicのUI ComponentはiOSとAndroidでそれぞれ異なる最適なマテリアルデザインが表示される
  • ネイティブ機能のAPIとドキュメントが豊富に公開されている

    • WEBベースで開発するが、ネイティブデバイスの機能もドキュメントを見てNative APIsを使えばハマらずに実装できる
    • Ionic Framework - Native APIs

Ionicの強み

  • Web系のフロントエンドエンジニアが学習コストなく活用可能
  • WEB APのロジックをそのまま使いまわせる
    • Flutterを採用する場合はDartで書き直さなくてはならない
  • 複数のWEBフロントフレームワークに対応
    • Angular, React, Vue, Nuxt.js
      • 日本ではなぜかReactが流行っているが、海外ではフルスタックなAngularやNuxt.jsが主流になりつつある認識
    • React NativeはReactにしか対応できない為

Ionic vs Flutter

  • 組織のエンジニアのスキルベースで判断
    • 軸がWEB(TS, JS) → Ionic
    • 軸がモバイル & 一からDartを学びたい → Flutter
  • WEB/モバイル以外にも対応したい場合はFlutter
    • FlutterはWindowsやmacOS、Linux、車載、テレビ、スマートホーム関連のモジュールに組み込むこともできる
  • すでにWEB APが存在していて、追加でモバイル対応したいケース
    • Ionicであれば、ロジックの書き直しが生じない
    • Flutterの場合はDartで書き直すことになる
  • 結局は新たにDartの記法を学ぶ必要があるか?が判断ポイント
    • 個人的には、WEBエンジニアはIonicで困ることが特に無い為、Flutterを覚える旨味が特に無いと考える

Ionic UI Component

Ionic UI Component

  • IonicのUI Componentを活用すれば、iOSとAndroidでそれぞれ異なる最適なマテリアルデザインを表示できる
    • それぞれデザインする必要は無く、デザイナー無しでも高いUI品質を担保できる

Ionic UI Template

Ionic FirstApp Temaplate

  • Ionicではテンプレートを活用することで、↑のようなUIを自動生成することができる
    • タブメニューやカメラ機能程度なら自分で実装する必要が無く、手軽にモバイルAPらしさを出せるので、WEB開発になれたエンジニアでも機能開発に集中できる

Ionicの学習方法

  • WEBの公式ドキュメントと書籍で基本的に困ることはない

書籍

以下がとても分かりやすかった

WEB

Ionic Document

後はベースに採用するWEBフレームワークの学習をすればOK


IonicによるAP開発手順

環境構築

  • Ionicの開発に必要なものは以下
    • Node.js
    • Ionic CLI

Node.js

Ionic CLIのインストール

  • Ionic CLI

    • これを入れるとIonicコマンドを実行可能になる
  • Nodeのバージョンが古い場合は先にバージョンアップ

  • Macの場合

    1
    2
    3
    4
    5
    sudo npm install ionic -g

    ...
    + ionic@5.4.16
    added 225 packages from 149 contributors in 10.316s
  • Windowsの場合

    1
    npm install ionic -g
  • バージョンを確認

    1
    2
    > ionic -v
    5.4.16

モバイルアプリ開発用ツールのインストール

  • 実機での動作確認やリリース作業(App Store/Google Play)に以下が必要
    • Xcode
      • iOSアプリの実行/リリースに必須の統合開発環境
      • Windowsでは利用不可
    • Android Studio
      • Androidアプリの実行/リリースに必須の統合開発環境
        Android Studio

Xcode

  • XcodeはApp Storeから入手
    • インストールには20分以上かかった

Xcode App Store

  • ランタイムのインストール
    1
    2
    3
    sudo gem install cocoapods # iOSのパッケージ管理システム
    xcode-select --install # Xcodeのコマンドラインツール
    pod repo update # CocoaPodsの依存関係を更新

Android Studioのインストール

  • Android Sudioサイトからダウンロード
    • インストーラーを起動してインストール
    • 指示にしたがって初期設定を実施

About Android Studio

Ionic向けのVSCode Pluginをインストール

  • VSCodeにはIonic/Angular向けのPluginが豊富にある
    • 必要に応じて開発環境に取り込む

Ionic projectの開始〜テンプレートからのUI自動生成

  • Ionic Projectを開始

    1
    ionic start --type=angular
  • Project名を入力

    1
    2
    3
    4
    5
    6
    Every great app needs a name! 😍

    Please enter the full name of your app. You can change this at any time. To bypass this prompt next time, supply
    name, the first argument to ionic start.

    ? Project name: sample-pj
  • 次にテンプレートを選択する

    • モバイルアプリのUIの大枠は自動構築できる
    • tabs
      • モバイルアプリの定番のタブ型のアプリ
    • sidemenu
      • サイドメニューがハンバーガーで開くタイプ
    • blank
      • Home画面のみのシンプルな画面
    • camera with gallery
      • カメラ機能とデバイスの画像を操作する機能があるタイプ
1
2
3
4
5
6
7
8
9
10
11
Let's pick the perfect starter template! 💪

Starter templates are ready-to-go Ionic apps that come packed with everything you need to build your app. To
bypass this prompt next time, supply template, the second argument to ionic start.

? Starter template: (Use arrow keys)
❯ tabs | A starting project with a simple tabbed interface
sidemenu | A starting project with a side menu with navigation in the content area
blank | A blank starter project
my-first-app | An example application that builds a camera with gallery
conference | A kitchen-sink application that shows off all Ionic has to offer
  • 指定したProject名のディレクトリが生成される

    1
    2
    > ls
    sample-pj
  • プレビューを起動

    • Angularのng serveと同様にlocalhostで動作確認可能
      1
      2
      > cd sample-pj
      ionic serve
  • Sidemenuテンプレートを使った場合の初期画面

    • メールアプリ風になっている

Ionic tabs template

  • Tabsテンプレートを使った場合の初期画面
    Ionic Tabs Tenplate
  • –labオプション
    • Ionic ComponentはiOSとAndroidでそれぞれ異なるマテリアルデザインを持っている
    • labオプションで同時に確認可能
      1
      ionic serve --lab
      Ionic Serve Lab

tabsテンプレートのコンポーネント構成

  • sample-pj/src/app
    • ディレクトリ構成は通常のAngular PJと同様
    • 以下のコンポーネントが自動生成されている
      • tabs, tab1, tab2, tab3, explore-container
1
2
3
app % ls
app-routing.module.ts app.component.scss app.component.ts explore-container tab2 tabs
app.component.html app.component.spec.ts app.module.ts tab1 tab3

UIの改修例

  • 自動生成されたUIの改修手順について
    • タブのアイコンやタイトルの変更

tabs component

  • デフォルト

    • tabs temlateで自動生成されるコード
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      <ion-tabs>

      <ion-tab-bar slot="bottom">
      <ion-tab-button tab="tab1">
      <ion-icon name="triangle"></ion-icon>
      <ion-label>Tab 1</ion-label>
      </ion-tab-button>

      <ion-tab-button tab="tab2">
      <ion-icon name="ellipse"></ion-icon>
      <ion-label>Tab 2</ion-label>
      </ion-tab-button>

      <ion-tab-button tab="tab3">
      <ion-icon name="square"></ion-icon>
      <ion-label>Tab 3</ion-label>
      </ion-tab-button>
      </ion-tab-bar>

      </ion-tabs>
  • 改修方法

    • アイコンを変更
      • 設計した各画面に適したion-iconを選定
    • タイトルを変更
      • 各画面のComponentのion-titleを変更
  • ion-iconについて

    • ion-iconのアイコンは以下のマテリアルデザインの名称をname=で指定することで活用できる
    • 各画面のアイコンとしてふさわしいものを公式ドキュメントから探せば良い
      ion-icons
  • tabs.page.htmlを以下のように実装
    • タブの一つをホーム画面とする
      • アイコンを家マークに変更
      • アイコン下のタイトルをHomeに変更
1
2
3
4
5
6
7
<ion-tabs>

<ion-tab-bar slot="bottom">
<ion-tab-button tab="tab1">
<ion-icon name="home"></ion-icon>
<ion-label>Home</ion-label>
</ion-tab-button>
  • serveで変化を確認

    • tabsのボタンのアイコンが切り替わっていることを確認
      1
      ionic serve --lab
  • ion-toolbarについて

    • 以下の書式でツールバーとタイトルを定められる
      1
      2
      3
      4
      5
      6
      7
      <ion-header [translucent]="true">
      <ion-toolbar>
      <ion-title>
      <!--アプリのタイトルに変更-->
      </ion-title>
      </ion-toolbar>
      </ion-header>

ここまでで、モバイルAPらしいタブ型の基本的なUIの骨子を開発することができる。この後、使えそうなUI Componentを公式ページから探して、各画面を作り上げていく。

デバイスのネイティブ機能の実装: Ionic Native API

Ionic - Native APIs

  • Ionic Native APIs
    • こちらを利用すればデバイス本体の機能を活用できる
      • ex. カメラ、センサー、写真フォルダ、3D Touch、クラウド連携…
        1
        CapacitorやCordovaを使って、どんなIonicアプリにもネイティブデバイスの機能を簡単に追加できるようにするための、オープンソースのコレクションとプレミアムプラグインと統合します。このことによって、ネイティブパワーのアプリ体験を構築することができます。
    • ベースとするフレームワーク毎の説明がドキュメントとして公開されているので、そこで記法を確認して利用すれば良い
      • この豊富なAPI群により、WEB開発しか経験の無い方でも、大抵のモバイルAPを開発できる

その他の実装については、ベースとしたWEBフレームワークやTypeScriptの書式を調査しながら進めれば良い。


参考

関連記事

その他

[Ionic Framework] Web/iOS/AndroidアプリのBuild方法

前提: モバイルの動作確認

  • 以下のコマンドでiOS/Andoridのそれぞれの画面を動作確認できる
    1
    ionic serve --lab

Ionic FirstApp Temaplate

Ionic ProjectのBuild

  • 単一のIonic Projectから、三種のAPをビルドできる
    • WEB(SPA), iOS, Android
  • 生成されたソースを各アプリストアで公開する

WebアプリとしてBuild

  • ここはベースとするフレームワークと同様

    • /dist配下にSPAとして動作するソースが払い出される(Angularベースの場合)
    • S3やFirebase Hostingに置けばサービス公開できる
  • WEB APのビルドコマンド

    1
    ionic build --prod

モバイルアプリとしてBuild

buildに必要なパッケージをPJにインストール

  • /wwwディレクトリが生成される
    1
    ionic integrations enable capacitor

iOS

  • buildしてiOSアプリとして動かす
    • /iosディレクトリが生成される
    • ここのソースをApple Storeに公開すれば良い
      1
      ionic cap add ios

以下はMac前提。IonicならMacでなくとも開発自体は問題なくできるが、動作確認をするにはMacである必要がある

Android

  • /androidディレクトリが生成される
    • ここのソースをGoogle Playに公開すれば良い
      1
      ionic cap add android
  • Android Studioを起動して実機確認

参考

関連記事

その他

[AWS/IaC] Former2によるCloudFormation/Terraformテンプレートの自動出力方法

AWSアカウント内の既存のリソースから、IaCテンプレート(CloudFormation/Terraform)を自動で作成するツールの活用方法と手動で改修すべき範囲についてのメモ

1. Former2の概要

Former2

  • Former2
    • ブラウザで動くWebアプリ
    • サードパーティ製のツール
      1
      Former2では、AWSアカウント内の既存のリソースからInfrastructure-as-Codeの出力を生成することができます。AWS JavaScript SDKを使用して関連するコールを行うことで、Former2はインフラストラクチャをスキャンし、出力を生成するリソースのリストを表示します。
    • CFnテンプレートだけでなく、Terraform等も出力可能
      • CloudFormation
      • Terraform
      • Troposphere
      • CDK (Cfn Primitives) - TypeScript, Python, Java, C#
      • CDK for Terraform - TypeScript
      • Pulumi - TypeScript
      • Diagram - embedded version of draw.io
    • CloudFormerと比較して高機能で2021年現在主流になっている
    • 価格はOSSのため無料
      1
      Former2はローカルでのアクセスや利用は無料ですが、一部のAWSサービスではAPIコールに関連して少額の料金が発生するため、AWSの利用料金に数セント余分に加算される可能性があります。
    • 出力したCFn Templateを汎用的にするには少し手直しが必要であった
      • ex. 固定値のパラメータを入力値を参照するように変更、formerでテンプレートに取り込めない細かいリソースの追記
    • 使用方法は二つ
      • Webアプリとして使用
      • セキュリティの関係でローカルホスティングしたい場合は、自分で動作環境を準備してそこで動かば良い

2. (Former2動作環境の起動)

3. Former2 Setup

3.1. Introduction

Former2 Introduction

  • ブラウザにプラグインを導入

    • Install Former2 Helper for Google Chrome
      Former2 Google Chrome Plugin
  • Continue to Credentialsを押下

3.2. Credentials

Former2 Credential

事前にReadOnlyAccessを付与したIAM UserとAccess Key/Secret Access Keyの用意が必要

  • IAM UserのCredentialを入力

    • Access Key ID
    • Secret Access Key
  • Credentialを正しく認識できたら、以下が表示される

    1
    Logged in as: <IAM User Name>@<AWS Account>
  • 公式説明

    1
    2
    リクエストを認証するには、IAM キーのペアが必要です。リソースを直接インポートすることを計画していない場合は、これらの資格情報で読み取りアクセスのみを提供し、
    ReadOnlyAccessポリシーを割り当てることをお勧めします。インポート機能を使用する予定がある場合は、スタックを作成するために適切な権限を付与する必要があります。
  • Continue to Parametersを押下

3.3. Parameter

Former2 Parameter

  • 独自のCloudFormation Parameterを設定する事ができる
    • 特になければスルー
1
オプションとして、以下にCloudFormationスタックのパラメータを追加することで、独自のCloudFormationスタックパラメータを含めることができます。デフォルト値が設定されている場合、値が一致していれば、出力でこれらのパラメータを参照するために !Ref または !Sub を使用することができます。
  • Continue to Settingsを押下

3.4. Settings

  • CFnテンプレートを出力するだけであればデフォルトでもOK

    • 設定後にGo to Dashboardを押下
  • CloudFormation Spacing

    • 出力のスペース数を変更
  • Logical ID Strategy

    • 論理ID名の付け方を設定
  • Default Output

    • 出力言語の設定
    • デフォルトではCloudFormation
    • ここで、TerraformやCDK, Draw.ioのDiagram等に変更可能
  • Irrelevant Resources

    1
    有効にすると、この設定は無関係とみなされるリソースをスキップします(現在はCloudWatchログストリームのみ)
  • Enable Related Resources

    • 関連リソースを有効化
  • Add All Resources

    1
    以下のボタンを使用して、スキャンしたすべてのリソースを出力に追加します(非推奨)
  • Save / Load Settings

    1
    すべての設定されたパラメータと設定(資格情報を除く)を含むファイルを保存またはロードします。
  • Programming Language

    1
    CDKやPulumiの出力プログラミング言語の好みを変更してください。
  • Default Resources

    1
    この設定を有効にすると、デフォルトのVPCやそのサブネットなどのデフォルトのリソースが含まれます。

以上でセットアップは完了

4. Former2によるテンプレートの出力

テンプレートに含めたいリソースを選択していく

  • Former2のDashboardからサービスを選択

Former2 Dashboard

  • 表示されたオブジェクトから、テンプレート化したいリソースを選択

  • +Add Selected

  • 関連するリソースがタブでまとまっており、直感的に操作できる
    Former2 Select Objects

  • Generate Template

    • オブジェクトの追加完了後に、Generate
    • CFnテンプレートが出力される
    • オブジェクト追加時に確認出来なかった項目も反映されていた
      • ex. tag

generate template

5. Former2で出力されるテンプレートについて

  • Former2では、一部CFnテンプレートに含まれない情報を見受けられた
  • また、各Propertyが固定された状態で出力されてしまうため、汎用的なテンプレートにするには入力値(Properties)の設定が必要になる

例とするPF構成

  • 以下のPF構成をformer2でテンプレート化したケースを考える
    • S3 Bucket
      • アプリをホスティング
    • CloudFront Distribution
      • S3 Bucketをソースとしてアプリを配信
    • AWS WAF
      • CloudFrontにアタッチ
      • アクセスを制限

出力したCFn Temlateに含まれない情報/注意点

  • s3 bucket

    • LifecycleConfiguration
      • Rules
        • Id指定のみで、ルール自体はCFn Templateに取り込めていなかった
        • 別アカウントでテンプレートを実行する場合は同名のルールが無いためエラーになってしまうと思われる
        • ライフサイクル未指定であれば無関係
  • WAFに関する注意点

    • CloudFront用に設定したWAFのリソースはGlobal(CloudFront)として扱われる
    • former2では画面右上のボタンでリージョンを選択するが、Globalが無い
    • 調査したところ、リージョンをUS East(N. Virginia)に設定することで確認可能であった
      • US East(N. Virginia)で生成される仕様になっていため、former2の画面右上のボタンでリージョンを変更するだけで良い

汎用的なテンプレートにするには

  • Parametersで各固定値を自由に設定可能な入力値に変更する

    • CFn Template実行時の入力値としてユーザが設定可能なパラメータを規定できる
      • テンプレートが汎用的に利用可能になり、別Projectやアカウントでも使い回し可能になる
    • 各パラメータには以下を定めることで利便性を高めることができる
      • Description
        • 説明を定義
      • 初期値とバリデーションも定義できる
        • 参考となるような名称を予め定めておくことで、Templateの利便性が高まる
    • 記載例
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      Parameters: # 入力値を定義 
      BucketName:
      # CFn実行時に入力欄に表示されるユーザ向けの説明
      Description: Please write bucket name
      # デフォルト値
      Default: sample-bucket #
      # 入力値のタイプ
      Type: String
      XXXXName:
      ...
  • 各リソースのPropertiesの各項目から入力値(Parameters)を参照する

    • !RefでParametesで定めた入力値を参照できる
    • 入力値の規定/参照例
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      Parameters: # 入力値を定義 
      BucketName:
      # CFn実行時に入力欄に表示されるユーザ向けの説明
      Description: Please write bucket name
      # デフォルト値
      Default: sample-bucket #
      # 入力値のタイプ
      Type: String
      ...
      Resources: # 生成するResourceを定義
      S3Bucket:
      Type: "AWS::S3::Bucket"
      Properties:
      BucketName: !Ref BucketName # 入力値を参照
  • 入力値の参照にはRefを用いる

    • 指定した論理IDのパラメータやリソースを参照する
      1
      !Ref <PropertyName>
      CloudFormationRef
  • Subを使うと変数と文字列を組み合わせられる?

  • !Subを活用すると、入力値と固定値を組み合わせることができる

    • 名称の一貫性や、入力の手間の省力化を見込める
    • 書式
      1
      2
      3
      !Sub "${PJName}-Bucket"
      !Sub "${PJName}-Policy"
      ...

所感

  • Former2で出力したテンプレートの活用方法

    • そのまま固定値で使用しても構わないが、他のPJでも使いまわせる汎用的なテンプレートにするには、paramaterで変数化する必要がある。CFnの記法については最低限知識が必要
  • CFnに精通したメンバがいない場合は、以下の流れでIaC化を図ると良さそう

    1. 0から無理にCFnテンプレを作らず、コンソールやCLIで検証しながらPFを構築
      • 初級者が0からCFnテンプレを書くと細かい内容でハマってしまう(実体験)
    2. ある程度定まってからFormer2で固定値のテンプレートを出力
    3. 固定値を変数化
    4. 以降はCFnテンプレートとGitでPFを管理していく
  • テンプレートの分割について

    • 大規模なPFの場合はテンプレートを分割して、他のテンプレートと入れ子構造にした方が良い
      • Former2でホスティング環境/IAM関連/運用監視関係と分割してテンプレートを出力することで、管理が円滑になる
    • 分割テンプレートを連携させるには、パラメータの受け渡しなどの手間が増えるため、CFnの知識がある程度必要

参考

関連記事

その他

[Angular] build時のError: initial exceeded maximum budget の解消方法

Angular 11 buid error: Index html generation failed.Inlining of fonts failed. An error has occurred while retrieving https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap over the internet.

  • Angular 11で有効化されたフォント最適化機能について
  • プロキシ環境下で開発をしている場合、ng buildでプロキシエラーが発生してしまう

発生した事象

  • Anular11 へのアップデート後に、ng buildで以下のエラーが発生する
    1
    2
    3
    4
    5
    6
    7
    > ng build --prod
    √ Browser application bundle generation complete.
    √ ES5 bundle generation complete.
    √ Copying assets complete.
    × Index html generation failed.
    Inlining of fonts failed. An error has occurred while retrieving https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap over the internet.
    connect ETIMEDOUT

原因

  • Angular 11からインラインフォントへの新機能が追加されている
  • プロキシ環境下で開発を行っている場合は、build時にプロキシを通過できずエラーが発生してしまう
    • 正しくHTTP_PROXY/http_proxyの環境変数を設定していても無視されてしまう

回避策

  • 幾つかGitのissueに同様の事象が挙がっていたが、以下が結論であった

    1
    ビルドサーバーはファイアウォールの外にはアクセスできません。それをオフにするのが唯一の選択肢だと思います。
  • font optimizationをオフにする方法

要約

  • 設定ファイル(angular-pj/angular.json)でbuildの設定を変更することで解決可能

  • デフォルトの設定

    • “optimization”: trueとなっている
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      "projects": {
      "app_name": {
      ...
      "architect": {
      "build": {
      "builder": "@angular-devkit/build-angular:browser",
      "options": {
      ...
      },
      "configurations": {
      "production": {
      "fileReplacements": [
      {
      "replace": "src/environments/environment.ts",
      "with": "src/environments/environment.prod.ts"
      }
      ],
      "optimization": true,
  • angular.jsonの各項目について

    • “architect”
      • コンパイルやテスト実行などの複雑なタスクを実行するためにCLIが使用するツール
      • ターゲットの設定に従って、指定されたタスクを実行するために指定されたビルダーを実行するシェル
    • “build”
    • “configurration”
      • このセクションでは、異なる目的地のための代替構成を定義し、名前を付ける。このセクションには、それぞれの設定のためのセクションがあり、その環境でのデフォルトのオプションを設定する。
    • “optimization”
      • より細かい設定を行うために、ブール値かオブジェクトのいずれかを指定できる。このオプションを使用すると、ビルド出力のさまざまな最適化が可能になる
      • fonts
        1
        2
        フォントの最適化を有効にします。
        注意: これにはインターネットへのアクセスが必要です
      • fonts optimsization options
        • inline
          1
          2
          アプリケーションの HTML インデックス ファイル内の外部の Google フォントとアイコンの CSS 定義をインライン化することで、レンダリング ブロック要求を減らします。
          注意:これにはインターネットへのアクセスが必要です。
  • 以下に変更

    1
    2
    3
    "optimization": { 
    "fonts": true
    }
  • 以上でbuildが問題なく実行できた

    1
    2
    3
    4
    5
    > ng build --prod
    √ Browser application bundle generation complete.
    √ ES5 bundle generation complete.
    √ Copying assets complete.
    √ Index html generation complete.

同様の事象はgitのissueで複数確認できたが、無効化する以外の解決策は見つけられなかった。

参考

関連記事

その他

[Angular] ng build時のエラー --aotについて

  • Angular 9以降でデフォルト化されたAOTについてのメモ
    • 8以前のバージョンからアップデートした際に注意すべきポイント

発生する事象

  • Angularのバージョンアップ後、build実行時にエラーが発生する
    • –aot オプションが必要になる
      1
      ng build --aot

原因/AOT

  • Angular9 以降ではahead-of-time (AOT)コンパイラがデフォルトに採用されている

  • Ahead-of-time (AOT) コンパイラ

    • 引用
      1
      2
      3
      Angular の ahead-of-time (AOT) コンパイラ は、ブラウザがそのコードをダウンロードして実行する前に、
      ビルドフェーズ中にAngular HTML コードと TypeScript コードを効率的な JavaScript コードに変換します。
      ビルドプロセス中にアプリケーションをコンパイルすると、ブラウザでのレンダリングが速くなります。
  • Angularには、アプリケーションをコンパイルする2つの方法がある

    • Just-in-Time (JIT)
      • 実行時にブラウザ内でアプリケーションをコンパイルします。This was the default until Angular 8.
    • Ahead-of-Time (AOT)
      • ビルド時にアプリとライブラリをコンパイルします。This is the default since Angular 9.
  • ng build (build only)またはng serve (build and serve locally)のCLIコマンドを実行すると、
    コンパイルの種類(JITかAOTか)は、angular.jsonで指定したビルド設定のaotプロパティの値に依存する

    • Angular 8以前で開始した場合はデフォルトでtrueではない

これらの要因により今回のエラーが発生したと思われる

aotの有効化

  • 設定を変更することで、buildの度にオプション(–aot)を付与せずともエラーを回避可能になる

  • 以下の設定ファイルでbuildの設定を変更することで解決できる

    • aotをtrueとするだけ
  • angular-pj/angular.json

    1
    2
    3
    4
    5
    6
    7
    8
    9
    "build": {
    "builder": "@angular-devkit/build-angular:browser",
    "options": {
    "outputPath": "dist/reacq-pj",
    "index": "src/index.html",
    "main": "src/main.ts",
    "polyfills": "src/polyfills.ts",
    "tsConfig": "tsconfig.app.json",
    "aot": true, //こちらを変更

参考

関連記事

その他

[Angular] An unhandled exception occurred: Port 4200 is already in use の解決策

ng serve実行時のエラーについてのメモ

Error

  • 以下のエラーが出る
    1
    2
    > ng serve
    An unhandled exception occurred: Port 4200 is already in use. Use '--port' to specify a different port.

解決策

Port 4200のプロセスをkillする

  • ポートが:4200と表示されているプロセスを探す
    1
    2
    3
    > netstat -a -n -o
    プロトコル ローカル アドレス 外部アドレス 状態 PID
    TCP 127.0.0.1:62279 127.0.0.1:4200 ESTABLISHED 18492
  • pidを指定してプロセスをkill
    1
    2
    > taskkill -f /pid 8464
    成功: PID 18492 のプロセスは強制終了されました。
  • 以上でserve 可能になる
    1
    > ng serve

参考

関連記事

その他

[Angular x AWS SDK for JavaScript] SPAからAWSのデータを取得する際の待ち合わせ処理(非同期処理)

  • SPAのプラットフォームにAWSを採用した際によく書くロジックについてのメモ

    • ex. クライアントからs3 bucketのobjectを取得して画面に表示
  • AWS SDKでデータを取得するまではディベロッパーガイドを見れば容易だが、待ち合わせ処理の書き方が少し面倒

前提知識


実装例

AWS SDK for JavaScript

  • 想定する状況

    • Angularのコンポーネント生成時(画面遷移時)に自動でAWS(S3)から情報を取得して画面に表示する
  • AWSからデータを取得して値を取り出すまでを待ち合わせ処理にする例

    1. ComponentからServiceのロジックを呼び出す
    2. Service内でAWSからデータを取得
      • 変数 = new Promise() の形式で、AWSへ問い合わせるロジックを書く
        • resolve(data)で変数に返り値を格納
      • 各関数間の待ち合わせ処理は async/await で実現する
    3. Promise型の返り値をComponentへ返す
    4. Component側で forkJoin().subscribe を使って受け取る
      .then()で受け取ってもOK (Angularでは基本subscribeが推奨されている)

書き方は他にもあるかも

AWS SDKを導入するまで

sampleの構成

  • request.service.ts

    • AWSとの通信用のService
  • sample.component.ts

    • Serviceを呼び出し、返り値を受け取るComponent
  • sample.component.html

    • 取得したデータを表示する

AWSへリクエストするService

以下の様に書かないと、データが帰ってくる前にcomponentにundefinedを返してしまう

  • AWSへのリクエストからの待ち合わせで返り値を取り出す方法

    • Promiseの中でリクエストする
    • GETしたデータをresolve()で返す
    • resolveで返された値をreturn
  • async/await でService内の各関数間の待ち合わせ処理を実現する

  • Service記載例 (request.service.ts)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    import { Injectable } from '@angular/core';

    // aws-sdkを変数AWSとしてimportする必要がある
    import * as AWS from 'aws-sdk'

    @Injectable({
    providedIn: 'root'
    })

    export class requestService {
    constructor() { }

    // 1. Componentから呼び出される関数
    async getAwsData() {
    return await this.requestData();
    }

    // 2. AWSに対してリクエストする関数
    async requestData() {

    // 変数requestに返り値を格納 待ち合わせのためにPromiseを用いる
    const request = new Promise((resolve, reject) => {

    // AWS SDKサービスのオブジェクトを宣言
    let bucket = new AWS.S3(({params: {Bucket: 'myBucket'}});

    // リクエストを実行 基本的に以下の様にdataに返り値が入る
    bucket.listObject(({}, (err, data ) => {
    if (err) {
    let errorMessage = String(err);
    alert(errorMessage);
    }
    else {
    // resolveでPromiseの処理を終了させる
    resolve(data); // データ加工が必要であれば、ここで関数を呼び出す
    }
    });
    });

    // エラーハンドリング
    request.catch((error) => {alert(error); } );
    // リクエスト結果を返す
    return request;
    }

    }
  • s3のデータの扱い方についてはディベロッパーガイドを参照。ここではあくまで共通した待ち合わせ処理の記法のみ


データを受け取るComponent側

  • Component側の受け取り方は2パターンある

    1. .then()
    2. .subscribe()
  • Component側の実装例(sample.component.ts)

    • serviceを呼び、データを受け取る
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//rxjs 待ち合わせ処理に必要
import { forkJoin } from 'rxjs';

let awsData; //データ受取用の変数を宣言

// 起動時に取得 → ngOnInitから呼び出す
ngOnInit() {
getData();
}

getData() {

// Credential認証 Service側でもいいかも
AWS.config.credentials = new AWS.Credentials("<access key>","<secret key>")

// thenで受け取る例
this.requestService.getAwsData().then(response =>{
console.log(response);
this.awsData = response; // 取得データを代入
});

// subscribeで受け取る例 Promise型のため、forkJoinが必要
forkJoin(this.grequestService.getAwsData()).subscribe(result => {
this.awsData = result[0]; // 取得データを代入
});
}
  • sample.compoent.html 取得したデータを画面に表示
    1
    {{getAwsData}}

参考

関連記事

その他

[Angular/TypeScript(JavaScript)] 非同期処理/待ち合わせ処理のまとめ (Observable/subscribe/forkJoin/Promise/async/await/then)

  • Angular等のTypeScriptベースのフレームワークでフロントエンドを開発する際には、待ち合わせ処理が度々問題となる
  • 外部通信が基本的に非同期処理であるため、返り値を加工するにはひと手間加える必要がある

前提知識

同期/非同期/待ち合わせ処理とは?

プログラムを書く前にどれが最適化か?を判断する必要がある(書き方が変わってくる)

  • 非同期処理

    • 実行完了を待たずに次の処理を並列で開始する
    • メリット
      • 処理の高速化を図れる。
      • 一部の処理でエラーが出ても、他の処理は問題無く動く
      • 処理完了までユーザーを待たせない
    • デメリット
      • 返り値を使って次の処理を行いたい時に問題が発生する
        (返り値無しの状態で次の処理を実行してしまう)
  • 同期処理

    • 処理完了まで他の処理をストップする
    • メリット
      • 処理を順に実行できるため、プログラムを書きやすい
    • デメリット
      • 処理完了までユーザーを待たせる事になる
  • 待ち合わせ処理

    • ある条件が満たされるまで処理を待たせる
      • ex. 返り値が戻ってくるまである処理の開始を待たせる
    • 同期処理とイコールではない
    • 複数の処理群としてみると非同期でOKだが、個々の処理は待ち合わせて順番に実行させたいケースがある
      • ユーザーが処理の完了を待つ必要がない → 非同期でOK
      • データ取得後に加工したい → 待ち合わせ処理が必要
  • TypeScript(JavaScript)においては、以下のような処理が非同期で実行されることを考慮する必要がある

    • API通信
    • データベース通信
    • その他の重い処理全般

TypeScript(JS)の待ち合わせ処理問題とは

上述の非同期で動く特性から、初級者は以下の問題でハマることが多い

  • TypeScript(JS)では、プログラムが書かれた順に動くとは限らない

    • 基本的に非同期処理で動く
      • 非同期処理は重い処理の終了を待たずに、次の処理を進められるので、高速化という意味では有効だが、困るシーンもある
  • よくあるケース

    • 外部APIと通信してデータを取得する場合、その戻り値が帰ってくる前に次の処理に進んでしまう。そのため、以降の処理がデータ無しで行われてしまう
    • 正しく外部にリクエストできている筈が、データ=undefinedと出力されてしまう
      • この辺りはChromeの開発モードで出力順を見ると理解し易い
  • 回避策: 待ち受け処理/同期処理を実現する(返り値が来た後に次の処理を実行させるように書き換える)
    • 書き方は色々ある
      1. Observable, subscibeを活用して待ち合わせ処理を実現する
      2. Observable, forkJoin()を活用して待ち合わせ処理を実現する
      3. aync/awaitを利用して同期処理化する
      4. Promise, then()を活用して同期処理化する

Observable型を使って待ち合わせ処理を実現するパターン

  • Angularでは適したケースであれば、基本的にPromiseよりもObservableが推奨されている

  • service側の返り値をobservable型に定義、component側で.subscribe()で受け取り、その中に処理を書き込む

  • ポイント

    • Observableでも待ち合わせ処理は実現できるが、同期処理はできない
    • 同期処理についてはPromiseを使う
    • ストリームを扱える

1. Observable & subscribe()

  • Service側

    • 返り値をObservable型で定義
  • Component側

    • .subscribe()で返り値を受け取る
  • sample.service.ts

    • Observable型で返り値を返す
      1
      2
      3
      4
      5
      6
      getmethod(){
      return observable = new Observable<number>(observer => {
      // 外部APIとの通信処理

      });
      }
  • sample.component.ts

    • 書式:observable.subscribe()
1
2
3
4
5
6

this.sampleService.getMethod().subscribe(value => {

// 返り値を利用する処理をここに書く

});

2. Observable & forkJoin()

  • forkJoin()を活用すると、Observableの全ての処理の完了を待って次の処理を実行できる

    • ユースケース
      • 複数回外部APIにリクエストを実行して、全てのデータが帰ってきてから加工
  • 書式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 複数の外部リクエストを実行してobservable型で受けとる
    // 配列hogehogeの中身を一つずつ引数として渡して実行する例
    const observable = hogehoge.map(hoge => {
    return this.sampleService.getData(hoge).pipe(catchError(e => observableOf({"error": e})))
    });

    // 待ち合わせ処理
    forkJoin(observables).subscribe( response => {
    // 以降に処理を記載
  • 活用例は以下の記事に記載した


Promise型を使って同期処理化するパターン

3. async/awaitを活用する

  • asyncをつけた関数の返り値はPromise型となる

  • asyncをつけた関数はawaitで待てる

  • await

    • Promiseの値が取り出されるまで待つ
  • async

    • awaitキーワードを使っている関数の頭に付ける必要がある
  • import必要?

  • ポイント

    • 同期処理化したい関数の宣言時:頭にasyncを付ける
    • 同期処理化したい関数の実行時:awaitを付ける
    • awaitはasyncを付けた関数内でしか実行できない

Serviceで外部API通信を実行 ⇒ Component側の変数に受け取る例

  • sample.component.ts
    • awaitを付けた関数の処理を待つ
      1
      2
      3
      4
      5
      6
      let result; // 返り値を受け取る変数
      async ngOnInit() {
      // Service経由で外部APIからデータを受け取る
      this.result = await this.sampleService.getMethod()
      console.log(this.getData);
      }
  • sample.service.ts
    • 関数の頭にasyncを付ける
      1
      2
      async getMethod(){
      }

複数の関数を順に実行する例

1
2
3
4
5
6
7
8
9
10
11
12
13
// プログラムの大筋
async function main() {
const x = await getX()
const y = await getY()
console.log(x + y)
}
async function getX() {
return 1
}
async function getY() {
return 2
}
main()

アロー関数の場合はasyncをつける箇所が異なる

  • Angularの場合は基本的にアロー関数が推奨されているのでこの書き方も覚えた方が良い
1
2
3
4
5
6
7
8
// functionによる関数宣言
async function sampleFunc() {
// 処理内容
}
// アロー関数による関数宣言
const sampleFunc = async() => {
// 処理内容
}

4. Promise, .then()を活用する

参考

関連記事

非同期処理参考

AWS SDK for JavaScriptでRegionリストを取得

やりたいこと

describeRegionsメソッド

  • 使用可能なリージョンに関する情報を取得するには、describeRegions メソッドを呼び出せばよい
    • 戻り値
      • Aws::EC2::Types::DescribeRegionsResult オブジェクト
        • DescribeRegionsResult.Regionsに配列が格納されている
        • AWS.EC2.RegionList
          1
          2
          3
          4
          console.log(data.Regions); // dataに戻り値が格納される
          [object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]
          console.log(data.Regions[0]);
          [object Object]
        • EC2.Region.RegionName
          1
          2
          console.log(data.Regions[0].RegionName);
          eu-north-1

実装例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// aws-sdkを変数AWSとしてimport
import * as AWS from 'aws-sdk';

// RegionListを取得するメソッド
getRegions() {
// AWS SDKのEC2オブジェクトを宣言
let ec2 = new AWS.EC2({region: ap-northeast-1}); // Default Regionの指定が必須
// 問合せ(describeRegions)を実行
ec2.describeRegions({}, (err, data ) => {
if (err) {
let errorMessage = String(err);
alert(errorMessage);
}
else { // データ取得時
// Aws::EC2::Types::DescribeRegionsResult オブジェクトが戻り値としてdataに格納される
let regions = data.Regions.map((d) => d.RegionName); // ループでリージョン名を抽出した配列を生成
return(regions);
}
});
}

参考

関連記事

その他

@EventEmitter @Input @Output @ViewChild ACM AMP API Gateway AR AR.js AR.js Studio AWS AWS Amplify AWS Budgets AWS Cost Explorer AWS SDK for JavaScript AddThis Adobe XD Alexa Amazon CloudWatch Amazon Honycode Amazon SNS Android Angular Angular Material AngularFire AppSync Augury C CDN CI/CD Cloud Craft Cloud9 CloudFormation CloudFront CloudTrail Cognito Cost Anomaly Detection Cubase ai Cubasis LE DTM Disqus DynamoDB Elgato HD 60S Firebase Firebase Hosting Former2 Github Github Actions Github.com GithubEnterprise GithubPages Google Chrome Google Cloud Shell GraphQL Hexo Hosting IAM Ionic JSON JavaScript LadioCast Logging LowCode MFA MS Authentication MacBook Pro 16 Mind Node NETDUETTO Netflix Party Netlify Network NoCode Observable PO PdM Promise RPA ReactiveForm Recognition Route53 S3 SAM(Serverless Application Model) SAR SSL SYNCROOM Schematics ScrumInc Serverless Service Siri Sitemap Spark AR Steinberg UR44C Teams Touch Cast Studio Treetable TypeScript UI UI Bakery WAF WAFv2 WEB Page Dev WEB会議 WebAR WebSocketAPI Webhook Windows Power Automate Wireshark aot async/await aws config cloud9 display.land draw.io e2e test filter() forkJoin() google search console hexo-generator-amp iOS iPad Pro iPhone icarus map() mat input mat tree mat-checkbox mat-input mat-selection-list mmhmm ngFor plantUML popIn Aladdin2 then() vscode ”global is not defined” らくがきAR アジャイル アジャイル開発 クロスプラットフォーム ショートカット スクラム スクラム開発 テレワーク ファイル操作 ブラウザ型IDE プロダクトオーナー プロダクトマネージャー プロトタイピング リモートセッション 共同開発 双方向データバインディング 待ち合わせ処理 認定スクラムマスター 静的WEB Hosting 静的WEBサイトHosting
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×