マイクロ・フロントエンド
アプリケーションの複雑さが増すにつれて、フロントエンドもますます複雑になっています。マイクロ・フロントエンド・アーキテクチャは、マイクロサービス・アーキテクチャのように、個別にデプロイ可能な小さく疎に結合するコンポーネントを定義することで、複雑さを軽減しようとしています。 Things Cloudはマイクロ・フロントエンド・アーキテクチャの上に構築されています。
アプリケーションの複雑さが増すにつれて、フロントエンドもますます複雑になっています。マイクロ・フロントエンド・アーキテクチャは、マイクロサービス・アーキテクチャのように、個別にデプロイ可能な小さく疎に結合するコンポーネントを定義することで、複雑さを軽減しようとしています。 Things Cloudはマイクロ・フロントエンド・アーキテクチャの上に構築されています。
マイクロ・フロントエンドアーキテクチャを構築するには、いくつかの選択肢があります。
Things Cloudではすでにアプリケーションホスティング経由でマイクロ・フロントエンドを使うことができます。アプリケーションAPIを介して異なるフロントエンドをデプロイし、アプリケーションスイッチャーを介して切り替えることができます。Web SDKを使用すれば、すべてのアプリケーションで同じルックアンドフィールが保証されます。
Things Cloudはこのアプローチを使えるようにするさまざまなnpmモジュールを提供しています。例えば、APIクライアント、スタイリングされたデフォルトアプリケーションのみを使用したり、Things CloudコンポーネントライブラリからAngularモジュールとして異なる機能をインポートしたりできます。提供されているすべてのライブラリを見るには、Things Cloud npmオプション をご覧ください。
新しいプラグインの概念が Things Cloud に導入されました。これにより、実行時に Web SDK ベースのアプリケーションを拡張できるようになります。
Things Cloudはすでにマイクロ・フロントエンドアーキテクチャに基づいています。実際、サーバーサイド・オプションは当初からのプラットフォームの概念です。コンパイル時オプションは2018年に導入されました。しかし、アプリケーションのランタイム拡張のニーズもあり、それがThings Cloudに導入された理由です。詳細は以下のセクションをご覧ください。
プラグインは、実行時に動的に機能をロードし、Web SDKベースの Web アプリケーションを拡張するための新しい概念です。アプリケーションを拡張するには以下の手順をご覧ください。
これは基本的にスクリプト・インジェクションです。アプリケーションはプラグインからremoteEntry.js
というスクリプトをリクエストします。マイクロ・フロントエンドに関して言えば、呼び出しを実行するアプリケーションは"shell"と呼ばれ、そのスコープに"remote"を注入します。
?noPlugin=true
クエリパラメーターですべてのプラグインを除外することができます。これらのプラグインは、Web SDKに統合されているあらゆるコンセプト概念を使用することができます。デバイス上に特定のボタンを追加するだけのものから、独自のナビゲータ・ノード、ルート、コンポーネントを持つ完全な機能セットまであります。多くのオプションがありますが、それらはすべてWeb SDKの枠内にあります。Things Cloudは、ReactやVue等などはサポートしていません。他のフレームワークを使いたい場合は、Things Cloudのコンパイル時統合をご覧ください。 現在のAngularアプリケーションをビルドするのと同じ開発者体験を提供するために、オプションを制限することにしました。開発者として、c8cycliを使うことで、新しいアプリケーションを作るのとほぼ同じ方法で最初のプラグインを始めることができます。
簡単なウィジェットの作成方法、その構造がどのように見えるか、アプリケーションへの追加方法については、ダッシュボードにカスタムウィジェットを追加する > ウィジェットコンポーネントを作成するをご覧ください。 次のチュートリアルでは、マイクロ・フロントエンド・アーキテクチャーを使用してウィジェットをアプリケーションに追加する方法と、プロセスが前のプロセスとどのように異なるかを中心に説明します。
以下に示すコマンドを使用して、サンプルプラグインを作成するための複数ステップのプロセスを開始します。
c8ycli new
プラグイン名を選択します(例:widget-plugin)。
? Enter the name of the project: (my-application) widget-plugin
サンプルアプリケーションを作成するバージョンを選択します(例:“1018.106.0”、マイクロ・フロントエンドは"10.16.0.0"以上のバージョンでサポートされています)。
? Which base version do you want to scaffold from?
1015.0.372 (latest)
❯ 1018.106.0 (next)
1018.0.47
1017.0.151
1016.0.233
1014.0.359
other
プラグインのベースとなるアプリケーションテンプレートを選択します(例:widget-plugin)。
? Which base project do you want to scaffold from?
administration
application
cockpit
devicemanagement
hybrid
tutorial
❯ widget-plugin
数秒後、次のようなメッセージが表示されます。
Application created. Go into the folder "widget-plugin" and run npm install
アプリケーションフォルダに移動し、npm install
を実行します。
アプリケーションフォルダは、以下の例のようになります。 このチュートリアルでは、最も重要なファイルは package.json と README.md です。
app.module.spec.ts
jest.config.js
README.md
tsconfig.spec.json
app.module.ts
package.json
setup-jest.js
widget/
index.ts
polyfills.ts
tsconfig.json
これで、マイクロ・フロントエンド・アーキテクチャーを使用する最初のプラグインが作成されました。
シンプルなウィジェットとマイクロ・フロントエンド・アーキテクチャに従ってビルドされたウィジェット間には、いくつかの違いがあります。
最大の違いは package.json ファイルで、isPackage
, package
, exports
などのフィールドが配置されています。
以下のリストは、フィールドとその役割を示しています。
isPackage
: アプリケーションがパッケージであるかどうかを示す。マイクロ・フロントエンドを利用して追加されたウィジェットの場合、値にtrue
を設定します。package
: パッケージの種類(例:plugin
)exports
: 重要なフィールドです。シェルアプリケーションのwidget-pluginで利用可能になるAngularモジュールを定義します(README.mdファイルもご覧ください):
name
: エクスポートされたモジュール名(Example widget plugin)module
: Angularモジュールクラス名(WidgetPluginModule)path
: モジュールを含む TypeScript ファイルへのパス。ファイルはネストされているため、次のパスを使用します。./widget/widget-plugin.module.tsdescription
: モジュールの簡単な説明。contextPath
: コンテキストパスはプラグインがどのURLから読み込まれるかを示します。これはグローバル変数の生成にも使用されるので、有効な JavaScript 変数を選択してください。例えば、contextPath
は数字で始めてはいけません。コンフリクトを避けるために、コンテキストパスにはプレフィックスを付けるのがよい方法です(例:acme
)。プラグインを作成する場合、カスタムモジュールはこのアプローチの中核になります。エクスポートされたモジュールは、シェルと呼ばれるアプリケーションとプラグインをリンクするエントリポイントとして扱われます。既製の機能を含む必要があるいくつかのモジュールを作成して、エクスポートできます。
さらに、これらのモジュールは、遅延ロードモジュールのように動作します。一つの大きなパッケージとして事前に読み込まれるのではなく、オンデマンドで読み込まれる小さなパッケージのコレクションのようなものです。 各モジュールは、HOOKの概念によって追加機能を拡張することができます。詳しくは既存のアプリケーションを拡張しフックを使用するをご覧ください。例えば、プラグインはHOOK_NAVIGATOR_NODESを使用してナビゲーションメニューに別のエントリを追加することができます。詳細はナビゲーターノードをフックするをご覧ください。
また、ローカル開発サーバーの起動方法にも違いがあり、サーバーの役割については以下のステップをご覧ください。
新しいプラグインの作成プロセスを容易にするため、ローカルサーバーのコマンドに、すべてのリクエストをシェルアプリケーション「コックピット」に代理させる新しいフラグが追加されました。
npm install
を実行し、ローカルサーバーを起動します。
npm start -- --shell cockpit
次のような出力が表示されます。
Shell application: cockpit
http://localhost:9000/apps/cockpit/index.html?remotes=%7B%22widget-plugin%22%3A%5B%22WidgetPluginModule%22%5D%7D
リンク先では、コックピットのログイン画面に転送されます。 ログイン後、ダッシュボードに移動しウィジェットの追加をクリックします。その際、利用可能なウィジェットのリストからモジュールフェデレーションウィジェットを選択します。
ウィジェットの編集作業の残りの部分は、通常のウィジェットの編集作業と同じです。変更内容を確認するには、ブラウザを更新してください。
通常のウィジェットとマイクロ・フロントエンド・アーキテクチャー用に変更されたウィジェットのpackage.jsonファイルのもう一つの違いは、remotes
フィールドです。(以下の例をご覧ください。)
...
"remotes": {
"widget-plugin": [ // contextPath
"WidgetPluginModule" // module class name
]
}
...
remotes
フィールドは、モジュールをインポートするために使用されます。モジュールを正しくインポートするには、プラグインのコンテキストパス (package.json の contextPath
フィールド) に続いて、モジュールのクラス名を指定します。プラグインは、remotes
というフィールドを介してそれ自体をインポートします。
エクスポートされたモジュールの正確性を検証する最初のステップとしてお勧めします。これにより、アプリケーションのデバッグが容易になります。
独自のモジュールをインポートした後、npm start
を実行して、ローカルサーバーが起動するかどうかを確認します。
後でプラグインを確認するために、npm start -- --shell cockpit
を使用して、さまざまなシェルアプリケーションでローカルでテストすることをお勧めします。
ウィジェットのアップロードは、通常のウィジェットと同じです。 以下のコマンドを順番に実行してください。
npm run build
そして、
npm run deploy
コンソールのプロンプトに従って、アプリケーションをテナントにデプロイします。
アップロードされたウィジェット プラグインをコックピットアプリケーションのダッシュボードに追加するには、以下のステップを実行します。
管理アプリケーション > エコシステム > アプリケーション > パッケージ の パッケージ タブにアクセスし、プラグインの詳細を確認してください。
すでにカスタムコックピットアプリケーションがある場合は、その詳細ページに移動し、プラグインタブに移動します。widget-plugin
をインストールしてください。
コックピットアプリケーションの独自のバージョンがない場合は、管理アプリケーション >エコシステム >アプリケーションに移動し、アプリケーションを追加をクリックします。表示されるダイアログで、既存のアプリケーションを複製
を選択します。アプリケーションのリストからコックピット(登録済み) を選択します。名前、アプリケーションキー、パスなど、利用可能なフィールドを編集します。デフォルトの値を使用し、次に進みます。複製したアプリケーションにwidget-plugin
をインストールします。
これで、カスタムウィジェットは、コックピットアプリケーションのバージョンで利用できるようになりました。 ウィジェットの追加のリストに、新しく追加されたウィジェットが利用可能なダッシュボードに移動します。
widget-plugin
は、管理アプリケーションの中からインストールされました。これが、ウィジェットに関する通常のアプローチと新しいアプローチとの主な違いです。
マイクロ・フロントエンド・アーキテクチャーでは、アプリケーションが実行中(ランタイム)に新しい機能を追加することができますが、古いアプローチでは、アプリケーションがビルド(コンパイル時)にしか新しい機能を追加することができませんでした。
マイクロ・フロントエンドでは、アプリケーションが実行中(ランタイム)に新しい機能を追加することが可能であるのに対し、以前のアプローチではアプリケーションがビルド中(コンパイル時)にしか新しい機能を追加することができませんでした。
ブループリントは、プラットフォームによってホストされる複数のUI機能の組み合わせ(静的ファイル)であり、ゼロから新しいソリューションの足場として使用することができます。一方、パッケージはプラグインとブループリントの組み合わせです。ブループリントはプラグインもエクスポートできるため、それらを1つのパッケージにまとめてプラットフォームにデプロイできます。
c8ycli new
? Enter the name of the project: (my-application) package-blueprint
? Which base version do you want to scaffold from?
1015.0.372 (latest)
1018.106.0 (next)
1018.0.47
1017.0.151
❯ 1016.0.233
1014.0.359
other
? Which base project do you want to scaffold from?
cockpit
devicemanagement
hybrid
> package-blueprint
tutorial
widget-plugin
administration
数秒後、以下のメッセージが表示されます。
Application created. Go into the folder "package-blueprint" and run npm install
npm install
を実行アプリケーションフォルダーは、下図の例のようになります。 このチュートリアルで最も重要なファイルは_package.json_と_README.md_です。
app.module.spec.ts
jest.config.js
README.md
tsconfig.spec.json
app.module.ts
package.json
setup-jest.js
index.ts
polyfills.ts
tsconfig.json
これで、モジュール・フェデレーションを使用する最初のパッケージ・ブループリントが作成されました。
HOOK_STEPPER
はアプリケーションの最初のロード中にアプリケーションのカスタマイズを可能にするために追加で提供することができます。このオプションのステップでは、起動時にナビゲータを折りたたむかどうかをユーザーが選択できる小さなシングルステップの例を示します。
import { CdkStep } from "@angular/cdk/stepper";
import { Component } from "@angular/core";
import {
AlertService,
AppStateService,
C8yStepper,
SetupComponent,
} from "@c8y/ngx-components";
@Component({
selector: "c8y-cockpit-setup-step1",
templateUrl: "./setup-step1.component.html",
host: { class: "d-contents" },
})
export class SetupStep1Component {
config = {
rootNodes: [],
features: {
alarms: true,
dataExplorer: true,
groups: true,
},
hideNavigator: false,
userSpecificHomeDashboard: false,
};
pending = false;
constructor(
public stepper: C8yStepper,
protected step: CdkStep,
protected setup: SetupComponent,
protected appState: AppStateService,
protected alert: AlertService
) {}
async next() {
this.pending = true;
try {
const newConfig = { ...this.setup.data$.value, ...this.config };
await this.appState.updateCurrentApplicationConfig(newConfig);
this.setup.stepCompleted(this.stepper.selectedIndex);
this.setup.data$.next(newConfig);
this.stepper.next();
} catch (ex) {
this.alert.addServerFailure(ex);
} finally {
this.pending = false;
}
}
back() {
this.stepper.previous();
}
}
<form #stepForm="ngForm" name="form" class="d-contents">
<div class="container-fluid flex-no-shrink fit-w">
<div class="row separator-bottom">
<div
class="col-md-8 col-md-offset-2 col-lg-6 col-lg-offset-3 p-t-24 p-l-16 p-r-16"
>
<h3 translate class="text-medium l-h-base">Misc</h3>
<p class="lead text-normal" translate>
Miscellaneous settings for the current application.
</p>
</div>
</div>
</div>
<div class="inner-scroll flex-grow">
<div class="container-fluid fit-w">
<div class="row">
<div class="col-md-8 col-md-offset-2 col-lg-6 col-lg-offset-3">
<c8y-misc-config [config]="config"></c8y-misc-config>
</div>
</div>
</div>
</div>
<div class="card-footer separator d-flex j-c-center">
<button
class="btn btn-default"
type="button"
(click)="back()"
*ngIf="index !== 0"
translate
>
Previous
</button>
<button class="btn btn-primary" type="submit" (click)="next()" translate>
Save and continue
</button>
</div>
</form>
import { NgModule } from "@angular/core";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { AssetsNavigatorModule } from "@c8y/ngx-components/assets-navigator";
import { RouterModule as ngRouterModule } from "@angular/router";
import {
CoreModule,
BootstrapComponent,
RouterModule,
SetupStep,
HOOK_STEPPER,
Steppers,
gettext,
} from "@c8y/ngx-components";
import { SetupStep1Component } from "./setup-step1.component";
import { DatapointLibraryModule } from "@c8y/ngx-components/datapoint-library";
import { MiscConfigComponent } from "./misc-config.component";
@NgModule({
declarations: [SetupStep1Component, MiscConfigComponent],
imports: [
BrowserAnimationsModule,
RouterModule.forRoot(),
ngRouterModule.forRoot([], { enableTracing: false, useHash: true }),
CoreModule.forRoot(),
DatapointLibraryModule.forRoot(),
AssetsNavigatorModule,
],
providers: [
{
provide: HOOK_STEPPER,
useValue: [
{
stepperId: Steppers.SETUP,
component: SetupStep1Component,
label: gettext("Step 1"),
setupId: "exampleId",
priority: 0,
},
] as SetupStep[],
multi: true,
},
],
bootstrap: [BootstrapComponent],
})
export class AppModule {}
最初のアプリケーション起動時に、ユーザーは上記のシングルステップ・ウィザードを完了する必要があります。
シンプルなウィジェットとマイクロ・フロントエンドガイドラインに従ってビルドされたウィジェットには、いくつかの違いがあります。
最も大きな違いは package.json ファイルで、isPackage
や package
や exports
(現在のブループリントアプリケーションにはない) などのフィールドが配置されています。
以下のリストにフィールドとその役割を示します。
isPackage
: アプリケーションがパッケージであるかどうかを示します。マイクロ・フロントエンドを使用して追加されたウィジェットの場合、値を true
に設定すします。package
: パッケージのタイプ (例: blueprint
パッケージのタイプはプラグインでも問題ないです)。exports
: 重要なフィールド。widget-pluginがシェルアプリケーションで利用できるようにするAngularモジュールを定義します(README.md ファイルも ご覧ください)
name
: エクスポートするモジュールの名前module
: Angular モジュールクラスの名前path
: モジュールを含む TypeScript ファイルのパスdescription
: モジュールが何をするのかの簡単な説明contextPath
: コンテキストパスはブループリントがどの URL でロードされるかを示します。これはグローバル変数の生成にも使用されるので、有効な JavaScript 変数を選択してください。例えば、contextPath
は数字で始まってはいけません。コンフリクトを避けるために、コンテキストパスに接頭辞を付けるとよいでしょう(例 acme
)パッケージのアップロードは、通常のウィジェットの場合と同じです。 以下のコマンドを順次実行してください。
npm run build
と
npm run deploy
コンソールのプロンプトに従って、アプリケーションをテナントにデプロイします。
どのアプローチを使い、どのように開発ジャーニーを始めるかを決めるのは大変なことです。 もしあなたがパートナーや顧客であれば、マイクロ・フロントエンドのジャーニーに参加することをお勧めします。 マイクロ・フロントエンド・ジャーニーに参加することで、サイロ・ソリューションを構築する代わりにエコシステムを拡張し、利用することができます。 このセクションでは、このジャーニーのビジョンと、開発に関するベストプラクティスについて説明します。
正しいものを開発するためには、アプリケーションでどのようなユーザーをターゲットにしたいかを理解することがとても重要です。 マイクロ・フロントエンドのストーリーの場合、ターゲットとなるユーザーは、開発者の経験があまりないソリューションアーキテクトです。 アイデアはシンプルで、どのようなIoTソリューションでも、ユーザーはコーディングなしで数ステップでセットアップできます。 そのためのビルディング・ブロックはエコシステムによって開発されます。 手順は以下の通りです。
マイクロ・フロントエンドストーリーのユーザージャーニーは、セルフサービスのアプローチです。 開発者でない人があなたのニーズに合わせることができるアプリケーションを提供するのです。 アプリケーションの使い方は未知であり、ソリューションアーキテクト次第であることを前提としています。 そのため、これらのアプリケーションは汎用的な方法で設計され、可能な限り再利用できるようにしなければなりません。 古典的なアプリケーションを開発したいのか、マイクロ・フロントエンドを開発したいのかを決めるために、次のデシジョンツリーを使ってください。
Your use case needs a distinct design and components? --YES--> Consider a custom implementation.
|
NO
|
Your use case is very specific? --YES--> Consider a custom implementation based on the Web SDK.
|
NO
|
Your use case is an app that others can use? --YES--> Consider a micro frontend blueprint.
|
NO
|
Consider a micro frontend plugin.
デシジョンツリーは、一番上(最も労力がかかり、カスタマイズの度合いが高い)から一番下(最も労力がかからず、カスタマイズの度合いが低い)まで読むことができます。 例えば、すべてを自分で行うことを選択した場合独自のログインロジックを書く必要があり、データグリッド、ダッシュボード、アセットセレクタのような私たちの事前構築されたコンポーネントを使用することはできず開発工数は非常に高くなります。 代わりにWeb SDKを使用すれば、すぐに多くの機能を利用でき、アプリケーションをブランディングすることで、自社開発の製品のように見せることができます。 さらに、アプリケーションの設計図として顧客に提供し、顧客のニーズに応じてブランディングすることも可能です。
ただし、利用可能なブループリント (管理 > エコシステム > 拡張機能) またはデフォルトのアプリケーション (コックピット、デバイス管理、管理) のいずれかがすでにほぼあなたのケースに適合しているが、例として拡張機能がないような場合は、代わりにプラグインを実装してください。 プラグインは実装が簡単で、既存のアプリケーションのあらゆる部分を拡張することができます。さらに、それらの拡張機能を顧客に提供するつもりであれば、そうしてプラグインを共有することができます。
次のセクションでは、ブループリントとプラグインの開発者ストーリーを詳しく説明し、ベストプラクティスを提供します。
すべての開発者ストーリーは CLI ツールから始まります。
新しいアプリケーションの足場を作り、どのデモを使うか決めることができます。
例えば、プラグインの場合は widget-plugin
デモを試すことができます。
このセクションではその方法について詳しくは説明しません(以前のセクションですでに説明済みです)。
その代わりに、アプリケーションをプラグインやブループリントにするものと、その違いについて説明します。
まず第一に、通常のアプリケーション、ブループリント、プラグインに大きな違いはありません。 これらはすべてアプリケーション API を介してビルド、テスト、デプロイされます。 しかし、プラグインとブループリントはマニフェストファイルにいくつかの詳細情報を持っています。
マニフェストファイルには、package.json ファイルの c8y.application
プロパティに格納されているすべてのオプションが含まれています。
ビルド時に、このファイルは cumulocity.json ファイルにコンパイルされます。
cumulocity.jsonファイルを含むアーカイブをアップロードすると、その情報はアプリケーションAPIからもアクセスできるようになります。
アプリケーションがプラグインであるかどうか、そしてどのタイプであるかを示す2つの重要なプロパティがあります。 プラグインまたはブループリントに使用される追加のプロパティがあります。 以下のリストは、マイクロ・フロントエンドのマニフェストファイルに関連するプロパティです。
{
"c8y": {
"application": {
"isPackage": true,
"package": "plugin",
"exports": [
{
"name": "Example widget plugin",
"module": "WidgetPluginModule",
"path": "./widget/widget-plugin.module.ts",
"description": "Adds a custom widget to the shell application"
}
],
"remotes": {
"widget-plugin": [
"WidgetPluginModule"
]
}
}
}
}
このプロパティは、マイクロ・フロントエンドのステップバイステップの手順に詳しく説明されています。 ただし、意味のあるプラグインやブループリントを作成するために役立つ可能性のある追加のプロパティがあります。
c8y.application.noAppSwitcher
: アプリケーションスイッチャー(インストールしたアプリケーションを切り替えるための右上のUIコンポーネント)にブループリントやプラグインを表示しないようにするには、常にtrue
に設定する必要があります。version
: これはエンドユーザーに表示されるバージョンです。プラグインがインストールされている場合、このバージョンは固定され同じバージョンを2回アップロードすることはできません。semverを使用して、プラットフォームにバージョンを確認させます。description
: これは、ユーザーがあなたのアプリケーションについて最初に読む情報です。ユーザーが詳細ビューを開くよう促すために、あなたのアプリケーションを一文で説明してください。keywords
: アプリケーションをさらに分類するために使用できます。author
: プラグイン/ブループリントを作成したユーザーに通知します。license
: 使用されているライセンスについてユーザーに通知します。常にライセンス情報を提供してください。バージョン10.18.0から、すべてのコミュニティパッケージがライセンスの確認を求めるようになりました。repository
: ソースコードがホストされている外部リポジトリを指定する場合に使用します。homepage
: より詳細な情報が得られる外部アプリケーションを指定する場合に使用します。requiredPlatformVersion
: どのバックエンドのバージョンをサポートしているかを示すために使用します。さらに詳細なREADME.mdファイル内に情報を追加することができ、パッケージの詳細ビューに表示されます。 これらのパッケージがリリースされた成果物です。 準備ができたら、パッケージをプラットフォームにデプロイして、既存のアプリケーションを拡張したり(プラグインで)、新しいアプリケーションをインストールしたり(ブループリントで)できます。 次のセクションでは、パッケージとは何か、パッケージには何が含まれているかについて説明します。
ユーザーストーリーで指摘されているように、2種類のマイクロ・フロントエンドを開発することができます。ブループリントとプラグインです。 どちらもアプリケーションAPIにアップロードされ、APIによってホストされるアプリケーションです。 これらのホストされたアプリケーションは、複数のプラグイン、あるいはブループリントとプラグインの組み合わせを含むことができるので、‘packages'という新しい概念ユニットが導入されました。 パッケージでは、複数のプラグインやブループリントを1つのパッケージにバンドルし、それらのバージョンを提供することができます。最適なパッケージには以下が含まれます。
パッケージのアイデアは、ブループリント・アプリケーションで管理できる複数の所属プラグインをバンドルすることです。そのため、コックピットアプリケーションに複数のウィジェットを公開して情報(例:「無料駐車場」ウィジェット)を表示するスマートシティ管理アプリケーションが良いユースケースになります。
拡張方法は変わりません。コンポーネントライブラリ (ngx)で定義されているHOOK_インターフェースのどれでも使用できます。NavigatorNode、ActionBarItem、またはRouteです。
hook<<Name>>
という型付きヘルパー関数が追加されました。
例えば、プロバイダー内で hookNavigator()
を使うと、ノードをナビゲータにフックすることができます。プラグインは遅延ロードされたAngularモジュールです。 そのため、デフォルトのAngularモジュールを使い、デフォルトのデベロッパーストーリーで検証することができます。 Things Cloudはさらに2つの検証方法を提供しているので、アプリケーションの検証とデバッグには3つの方法があります。
Classic: アプリケーションを実行し、モジュールを AppModule にインポートします。それから c8ycli serve
を実行してアプリケーションを起動します。
Lazy loading: アプリケーションを実行しますが、モジュールはインポートしません。その代わりに、リモートにモジュールをインポートします(これは基本的にセルフインポートプラグインです)。c8y.application.remotes
オプションをモジュールに指定してください。
"remotes": {
"<<contex-path>>": [
"<<module-name>>"
]
}
これには、アプリケーションがシェルとして機能し、遅延ロードで問題が発生しそうな場所を確認できるという利点があります。
Shell: アプリケーションを実行し、c8cycli serve --shell cockpit
を実行して任意のシェルを指定してください。これは実際のアプリケーションでテストできるという利点があります。しかし、シェルアプリケーションはすでにデプロイされているので、役に立たないエラースタックが表示されるかもしれません。
上記の方法2、Lazy loadingの使用をお勧めします。 必要であれば、方法3でアプリケーションを検証してください。 次のセクションで説明する一般的な落とし穴にぶつかる可能性があるので、できれば方法1は避けてください。
避けるべき問題がいくつかあります。
ルーティング:一般的な名前のルートを避けてください。例えば/home
のようなルートは使用せず、代わりに /<<my-unique-prefix>>/home
を使用してください。一般的に名前が付けられたルートは、他のプラグインによって上書きされる可能性があります。開発プロセスで使用する識別子についても同じことを行います。
遅延ロード: すべてのプラグインは遅延ロードされることを覚えておいてください。これは、遅延ロードされたモジュールには遅延ロードされたモジュールのルールが適用されることを意味します。 ngx-components の CoreModule
や RoutingModule
では forRoot
を使用しないでください。新しく導入された依存関係には forRoot
を使用してください。
インジェクター: 遅延ロードのアプローチでは、インジェクターが問題になることがあります。通常、プラグインごとに新しいインジェクターを用意します。ファクトリー関数を使わずにフックを使う限り、これは自動的に行われます。ファクトリー関数を使う場合は、インジェクターを用意しなければなりません。
import { Injectable, Injector } from '@angular/core';
import { ActionBarItem, EmptyComponent } from '@c8y/ngx-components';
@Injectable()
export class MyActionBarFactory {
constructor(private injector: Injector) {}
get() {
const actionBarItem: ActionBarItem = {
component: EmptyComponent,
injector: this.injector, // (1)
placement: 'left'
};
return actionBarItem;
}
}
(1)
:これは、提供したコンポーネントが、あなたのプラグインでしか利用できないサービスを利用したい場合に重要です。インジェクターを定義しないと、ルートインジェクターを使用することになり、インジェクターの問題につながります。
アセットをバンドルするのは、コピーするだけでなくパスが正しく反映されていなければなりません。例えば、画像をインポートする場合、画像へのパスは次のようになります。http://<<instance>>/apps/<<context-path>>@<<version>>/my-image.png
デプロイするたびにバージョンを変更したくないかもしれません。
そのため、bundler にイメージを処理させることをお勧めします。
これは typescript ファイルにイメージをインポートすることで行います。
bundler は常にイメージへの正しいパスを返します。
例えば、以下のようなファイル assets/assets.ts を作成します。
import previewImage from './widget-plugin-pr.png';
export const assetPaths = { previewImage };
TypescriptはPNGファイルの扱い方を知らないため、エラーを投げます。 assets/index.d.ts*ファイルにモジュールとして宣言することで、typescriptにそのようなファイルを受け入れるように伝えてください。
declare module '*.png';
アセットをプラグインかブループリントのどこかにインポートし、パスを使用して画像を表示します。
import { assetPaths } from '../assets/assets';
console.log(assetPaths.previewImage);
assetPaths.previewImage
は任意のコンポーネントやフックで使用できるようになりました。
c8ycli new
で widget-plugin をスキャフォールドすると、完全な例を見ることができる。
なお、この機能と例はバージョン 10.17.0 で追加されました。
標準的なカスタムアプリケーションと同じように、翻訳もすぐに動作します。
リポジトリに .po
ファイルを追加し、index.ts ファイルにインポートしてください。
プラグインの翻訳は実行時にマージされるため、既存の翻訳を上書きするかもしれません。
例えば、widget-plugin を c8ycli new
でスキャフォールドした場合です。
制限があることに注意してください: プラグインは新しい言語を追加できません。プラグインが拡張できるのは、既存のアプリケーションで翻訳された文字列だけです。
ブランディングは完全にサポートされています。 アプリケーションにはコンポーネント・ベースのスタイリングを使用することをお勧めします。 しかし、インポートすることで、カスタムのグローバル CSS スタイリングを追加することもできます。
import './example.css';
c8ycli new
でwidget-pluginをスキャフォールドすると、またサンプルが作成されます。
一般的な互換性を確保する方法はありません。 すべてのメジャーなWeb SDKのバージョンには、新しいAngularのバージョンが含まれている可能性があります。 Angularはすべてのマイクロ・フロントエンドで共有されるライブラリの1つであり、そのため非推奨のメソッドを呼び出すと、マイクロ・フロントエンドとそれをインポートするアプリケーションの互換性が失われる可能性があります。 しかし、このような非互換性を回避するために2つのバージョン保護メソッドを用意しています。
アプリケーションやプラグインのアップデートは可能です。プラグインのアップデートについては、実際のアプリケーションをクローンし、プラグインが新しいバージョンでも動作するかどうかをテストすることをお勧めします。クローンされたアプリケーションはテストアプリケーションであり、その後削除することができます。
ブループリントについては、アプリケーションのアップデートを提案する通知が届きます。これらのアップデートはテストすることもでき、いくつかのプラグインが失敗した場合はロールバックすることができます。
バージョン10.19.0からは、プラグインのどのバージョンがWeb SDKのどのバージョンと互換性があるかを正確に示すバージョンマトリックスも追加されました。
プラグインの最新バージョンを常に提供したいプラグイン開発者には、community plugin Github project をお勧めします。このcommunity plugin Github project には、プラグインの最新バージョンがまだ動作することをテスト・検証するための CI/CD ワークフローがいくつか含まれています。
リポジトリコネクトは、プラグインまたはブループリントをThings Cloudプラットフォームのインスタンスと同期するマイクロサービスです。 管理テナントにインストールする必要があり、複数のリポジトリを接続できます。 現在、Software AG のパブリック GitHub のみが接続されています。 複数の方法でブループリントやプラグインに参加し、共有することができます。
同期のために、マイクロサービスは特定のトピックとリリース(NTT コミュニケーションズの場合は cumulocity-package
)を持つすべてのリポジトリを検索します。
リリースはプラグインまたはブループリントを含む単一のzipファイルでなければなりません。
スコープと呼ばれるセキュリティーメカニズムがあり、特定の接頭辞がないアプリケーションのアップロードを禁止します。
これは、同期されたパッケージがコックピットや管理画面のようなデフォルトのアプリケーションを上書きしてしまうのを防ぐためです。
key
と contextPath
の前に設定した接頭辞を付けます(NTT コミュニケーションズ の場合は sag-pkg
)。
リポジトリ接続経由でアップロードされるすべてのアプリケーションは community
プラグインと表示され、インストール時にライセンスと保守契約がユーザーに通知されます(バージョン 10.18.0 以降)。