使い方レシピ

このセクションは Angular 向け Web SDK の一般的な使い方レシピをリストアップしています。ここでは以下が必要となります:

  • Angular のコンポーネント、サービス、モジュールについて基本的な理解があること
  • アプリケーションのスキャフォールドの仕方と @c8y/cli を使ってアプリケーションを実行する方法を理解していること
  • @c8y/ngx-components の extension point の概念について基本的な理解があること

参考: 全てのレシピは Angular 向け Web SDK の特定のバージョンで書かれています。バージョンはレシピの上部に示されています。レシピで示されている機能の中には利用可能でないものもあるため、古いバージョンでの使用は推奨しません。レシピよりも新しいバージョンを使用する場合、名前やインポートするものに変更があるかもしれません。概念的な改訂がある場合はレシピを更新しますが、小さな変更での更新はありません。全てのコンセプトの最新の例を見るには、c8ycli new my-app tutorial でチュートリアルアプリケーションを確認してください。

既存のアプリケーションを拡張しフックを使用する

バージョン: 10.4.11.0 | パッケージ: @c8y/cli, @c8y/apps and @c8y/ngx-components

コックピットやデバイス管理のような既存のアプリを拡張するのは一般的なユースケースです。

このレシピでは、カスタムルートでコックピットを拡張する方法、ナビゲータにこのルートを追加する方法を順番に説明していきます。説明の前に、何をハイブリッドアプリケーションと呼んでいるのか、@c8y/apps npmパッケージが何を含んでいるのか、という背景を紹介します。

背景

デフォルトのアプリケーションはThings Cloudに同梱されている3つのアプリケーションで構成されています。これらのアプリケーションは何年も開発が続けられた結果、コードは主にAngularJSベースとなっています。現在、全ての組み込みアプリケーションではAngularを使用しているため、双方のフレームワークを提供する方法を見つける必要がありました。 @c8y/cli により、2つのフレームワークを共存させるデフォルトアプリをスキャフォールドすることが可能になりました。これは、AngularとAngularJSのアプリを同時に提供するためにAngularのアップグレード機能を用いています。これにより、新機能はAngularで開発でき、全てのAngularJSのプラグインは簡単に統合可能になります。これを私たちは ハイブリッド モードと呼んでいます。

しかし、ハイブリッドモードにはいくつか制限があります。このレシピの後半で説明します。これらの制限により、純粋で 空のスターターAngularアプリケーションを提供することにしました。これはAngularJSのプラグインを統合する可能性がありません。このAngularのみのアプリケーションは以下の2つを含みます:

つまり、Web SDKを始めるには3つの選択肢があります。既存のハイブリッドアプリを拡張する、Angular CLIでAngularのみのアプリを開発する、そして@c8y/cliを使ってアプリを開発する、の3つのケースです。どれを選択するかは開発したいアプリケーションに依ります。例えば、プラットフォームの見た目は同じですが特定のシナリオに対して特別な依存関係(例: マテリアルフレームワークなど)を使用したアプリケーションが必要な場合、Angular CLIを使用するのが最も良いです。

ほとんどの場合、後ほどこのレシピで紹介するハイブリッドアプリを拡張したいだけです。しかしはじめに、よりよく理解するためにそのアプローチの限界、またなぜコンセプトがこのように設計されているかという理由を示さなければなりません。

ハイブリッドモードの制限

ハイブリッドアプリを実行する時、AngularとAngularJSは並行して実行されるようにする必要があるため、いくつかの制限があります。以下のリストはその制限について説明しています:

制限について理解したところで最初のアプリケーションを拡張し、拡張用フックを開発することができます。これを行うには、ハイブリッドアプリをスキャフォールドする必要があります。ここで @c8y/apps パッケージを使用します。このパッケージにはデフォルトアプリとそれらの最低限のセットアップが含まれています。c8yclinew コマンドでアプリを初期化する度に @c8y/apps パッケージを使用します。次のセクションではこのプロセスを説明し、順を追ってハイブリッドアプリを拡張します。

1. サンプルアプリを初期化する

まずはじめに、アプリケーションが必要です。c8ycli を利用して新しくコックピットアプリケーションを作成します:

c8ycli new my-cockpit cockpit

次に、依存関係をインストールします。新しく作成されたフォルダに移動し npm install を実行してください。

ヒント: c8ycli new コマンドにはスキャフォールドに使用するパッケージを定義する -a フラグがあります。この方法でどのバージョンのアプリをスキャフォールドしたいか定義することもできます。例えば以下の通りです:

  • c8ycli new my-cockpit cockpit -a @c8y/apps@1004.11.0 はバージョン 10.4.11.0 のアプリをスキャフォールドします。
  • c8ycli new my-cockpit cockpit -a @c8y/apps@latest は 最新の公式リリースのアプリをスキャフォールドします。-a フラグ無しで使用するのと同様です。
  • c8ycli new my-cockpit cockpit -a @c8y/apps@next は最新のベータリリースのアプリをスキャフォールドします。

2. ルートにカスタムコンポーネントをバインドする

ルートはAnuglarと同様の方法で追加可能です。ハイブリッドの制限で示したように、UPGRADE_ROUTES の前に定義する必要があることが唯一の違いです。このため、以下の内容で簡素な hello.component.ts を作成することができます:

import { Component } from '@angular/core';

@Component({
  selector: 'app-hello',
  template: `
  <c8y-title>Hello</c8y-title> 
  World
  `,
})
export class HelloComponent {
  constructor() {}
}

これは非常に基本的なコンポーネントです。テンプレートがタイトルを表示するために"コンテンツ投影"と呼ばれる特別な機能を使っているのみです。コンテンツ投影は別の場所へコンテンツを表示して定義するAngularの概念です。どのコンポーネントがコンテンツ投影をサポートしているかは @c8y/ngx-components の文書に記述されています。

以下の方法で app.module.ts を変更することによりルートにカスタムコンポーネントをバインドできるようになります:

import { NgModule } from '@angular/core';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterModule as NgRouterModule } from '@angular/router';
import { UpgradeModule as NgUpgradeModule } from '@angular/upgrade/static';
import { CoreModule, RouterModule } from '@c8y/ngx-components';
import { UpgradeModule, HybridAppModule, UPGRADE_ROUTES } from '@c8y/ngx-components/upgrade';
import { AssetsNavigatorModule } from '@c8y/ngx-components/assets-navigator';

// --- 8< changed part ----
import { HelloComponent } from './hello.component';    // 1
// --- >8 ----

@NgModule({

  // --- 8< changed part ----
  declarations: [HelloComponent],                      // 2
  // --- >8 ----

  imports: [
    BrowserAnimationsModule,
    RouterModule.forRoot(),
    NgRouterModule.forRoot([
      // --- 8< changed part ----
      { path: 'hello', component: HelloComponent},     // 3
      // --- >8 ----

      ...UPGRADE_ROUTES
    ], { enableTracing: false, useHash: true }),
    CoreModule.forRoot(),
    AssetsNavigatorModule,
    NgUpgradeModule,
    // Upgrade module must be the last
    UpgradeModule
  ]
})
export class AppModule extends HybridAppModule {
  constructor(protected upgrade: NgUpgradeModule) {
    super();
  }
}

ここでの変更は単純です。まずはじめに、(1)コンポーネントをインポートします。(2)次に、そのコンポーネントを宣言部へ追加します。(3)最後にこの場合では hello というパスへバインドする必要があります。c8ycli server でアプリケーションを起動し、URLへハッシュを追加し(http://localhost:9000/apps/cockpit/#/hello)アクセスするとカスタムコンポーネントが追加されているでしょう。次のステップでは、左ナビゲータのコンポーネントを追加します。

3. ナビゲータのノードを追加する

ユーザが作成した hello.component.ts をナビゲート可能にするために、左サイドのナビゲータにナビゲーションを追加する必要があります。このためにフックを使用します。

フックは特定のインジェクショントークンにバインドされている単なるプロバイダーです。複数のプロバイダーを追加可能にするために、Angularのマルチプロバイダーの概念を使用します。詳細な説明はこのチュートリアルの範囲を超えていますが、公式の文書 に説明があります。

インジェクショントークンはインポートするだけで @c8y/ngx-components パッケージから受け取ることができます。これらは全て目的に応じて HOOK_ から始まります。ナビゲータノードを追加するには HOOK_NAVIGATOR_NODE を使用します:

import { NgModule } from '@angular/core';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterModule as NgRouterModule } from '@angular/router';
import { UpgradeModule as NgUpgradeModule } from '@angular/upgrade/static';
// --- 8< changed part ----
import { CoreModule, RouterModule, HOOK_NAVIGATOR_NODES, NavigatorNode } from '@c8y/ngx-components';
// --- >8 ----
import { UpgradeModule, HybridAppModule, UPGRADE_ROUTES } from '@c8y/ngx-components/upgrade';
import { AssetsNavigatorModule } from '@c8y/ngx-components/assets-navigator';
import { HelloComponent } from './hello.component'; 

@NgModule({
  declarations: [HelloComponent],                      

  imports: [
    BrowserAnimationsModule,
    RouterModule.forRoot(),
    NgRouterModule.forRoot([
      { path: 'hello', component: HelloComponent},     
      ...UPGRADE_ROUTES
    ], { enableTracing: false, useHash: true }),
    CoreModule.forRoot(),
    AssetsNavigatorModule,
    NgUpgradeModule,
    // Upgrade module must be the last
    UpgradeModule
  ],

  // --- 8< changed part ----
  providers: [
    {
      provide: HOOK_NAVIGATOR_NODES, // 1
      useValue: [{                   // 2
        label: 'Hello',              // 3
        path: 'hello',
        icon: 'rocket',
        priority: 1000
      }] as NavigatorNode[],         // 4
      multi: true                    // 5
    }
  ]
  // --- >8 ----

})
export class AppModule extends HybridAppModule {
  constructor(protected upgrade: NgUpgradeModule) {
    super();
  }
}

上記の数字の説明は以下のとおりです:

  1. HOOK_NAVIGATOR_NODES を提供します。
  2. 特定のvalueを使います。複雑なケースでは useClassget() を定義することもできます。
  3. どのようなナビゲータのノードが見えるべきかを定義します。
  4. フックのほとんどはTypeScriptで先行入力できるインタフェースを持っています。
  5. マルチプロバイダーフラグは一つ以上のフラグが存在するかどうかをAngularに伝えます。

この拡張用フックを実装すると、ナビゲータに新しいエントリーが増えます。そのエントリーは以下のように表示されます。( NavigatorNode インタフェースの priority プロパティはどの順番でノードが表示されるかを定義していることに注意してください。):

拡張したコックピットアプリケーション

hello.component.ts にあるように、コックピットアプリの中に空白のキャンバスが存在します。このため、コックピットの特定の機能は変更されずにどんな機能でも実装することができます。

結論

このレシピで見たように、ハイブリッドアプリにはAngularJSとAngularの統合による制限があります。しかし、フックの概念とカスタムルートにより既存のハイブリッドアプリにほとんどどんなものでも追加できるようになります。これらは組み込みアプリを拡張する強力なツールになります。しかし、より多くの機能が必要な場合、Angularのみのアプリの方が適している場合もあります。シンプルな拡張で十分か、もしくは新しいアプリケーションを実装する必要があるかを判断するにはユースケースに依存します。

コンテキストルートを使用して詳細ビューにタブを追加する

バージョン: 10.4.11.0 | パッケージ: @c8y/cli and @c8y/ngx-components

デバイスやグループの詳細画面でユーザに追加の情報を表示したい、というのは一般的なユースケースです。

このレシピではデバイス詳細画面に新しいタブを追加する方法を説明します:

カスタムタブでのデバイス情報

Angular向けWeb SDKでは、特定のコンテキストに対してのビューを提供するため、このような画面は ViewContext と呼ばれます。コンテキストビューにはいくつか種類があります。(例えば、DeviceGroupUserApplicationTenant など) ユーザはハッシュナビゲーションで特定の Route でナビゲートすることでコンテキストビューにアクセスできます。例えば、apps/cockpit/#/device/1234 へアクセスした場合、アプリケーションは 1234 のIDを持つデバイスを解決しようとします。

詳細ビューは/info と呼ばれる別のルートから参照されている上記のスクリーンショットの 情報 タブにあるように、通常いくつかの Tabs を表示していますが、情報を表示するためにデバイスのコンテキストを再利用しています。

以下では、apps/cockpit/#/device/:id/hello からアクセスできるこのビューに新しいタブを追加する方法を紹介します。

1. サンプルアプリを初期化する

はじめに、コンテキストルートをサポートするアプリケーションが必要です。このために、c8ycli を使用して新しいコックピットアプリケーションを作成します:

c8ycli new my-cockpit cockpit

次に、依存関係をインストールします。新しく作成されたフォルダへ移動し npm install を実行してください。

2. 新しいROUTE_HOOK_ONCEを追加する

フックの概念により既存のコードに追加できます。この例では、device/:id という既存のルートにいわゆるChildRoute(Angularによる)を追加します。

これを達成するために、app.module.ts に以下のコードを追加します:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouterModule as NgRouterModule } from '@angular/router';
import { UpgradeModule as NgUpgradeModule } from '@angular/upgrade/static';
// ---- 8< changed part ----
import { CoreModule, RouterModule, HOOK_ONCE_ROUTE, ViewContext } from '@c8y/ngx-components';
// ---- >8 ----
import { UpgradeModule, HybridAppModule, UPGRADE_ROUTES } from '@c8y/ngx-components/upgrade';
import { AssetsNavigatorModule } from '@c8y/ngx-components/assets-navigator';

@NgModule({
  imports: [
    BrowserModule,
    RouterModule.forRoot(),
    NgRouterModule.forRoot([
      ...UPGRADE_ROUTES,
    ], { enableTracing: false, useHash: true }),
    CoreModule.forRoot(),
    AssetsNavigatorModule,
    NgUpgradeModule,
    UpgradeModule
    ]

  // ---- 8< added part ----
  providers: [{ 
    provide: HOOK_ONCE_ROUTE,          // 1.
    useValue: [{                       // 2.
      context: ViewContext.Device,     // 3.
      path: 'hello',                   // 4.
      component: HelloComponent,       // 5.
      label: 'hello',                  // 6.
      priority: 100,
      icon: 'rocket'
    }], 
    multi: true
  }]
  // ---- >8 ----

})
export class AppModule extends HybridAppModule {
  constructor(protected upgrade: NgUpgradeModule) {
    super();
  }
}

上記の数字の説明は以下の通りです:

  1. マルチプロバイダーフックの HOOK_ROUTE_ONCE を提供します。これは現在のルートの設定を拡張するためにアプリケーションに指示します。
  2. ルートフックを定義するために値を使用することを明記します。例えばルートを非同期に解決したい場合、クラスを使用することもできます。
  3. ルートのコンテキストを定義します。定義するには ViewContext enum を使用する必要があります。この場合、デバイスのコンテキストを拡張します。
  4. 表示されるパスです。コンテキストパスに追加されます。この場合、完全パスは以下になります: device/:id/hello.
  5. パスがユーザによって入力されたらどのコンポーネントを表示するかを定義します。
  6. タブがどのように表示されるかを定義する label かつ icon プロパティです。priority はどの位置に表示するべきかを定義します。 The priority defines on which position it should be shown.

参考: HOOK_ONCE_ROUTE はAngular Routeの型を継承するため、全てのプロパティはここで再利用されます。

この一連の設定の後、ルートは登録されますが HelloComponent はまだ存在しないため、アプリケーションはコンパイルに失敗します。次のセクションで HelloComponent を作成します。

3. コンテキストデータを表示するコンポーネントを追加する

HelloComponent はデバイスの詳細を表示したい場合があるでしょう。したがってどのコンテキストがオープンになっているかという情報が必要になります。コンテキストルートはデバイスを前もって解決するため、これを処理する必要はありません。親のルートから直接アクセスできます。

hello.component.ts という名前の新しいファイルを作成しましょう:

import { Component } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({
  selector: 'app-hello',
  template: `
  <c8y-title>world</c8y-title>
  <pre>
    <code>
      {{route.snapshot.parent.data.contextData | json}}
    </code>
  </pre>
  `
})
export class HelloComponent {
  constructor(public route: ActivatedRoute) {}
}

ActivatedRoute を注入して親のデータにアクセスする以外は、このコンポーネントについて特別言及することはありません。これはキーとなるポイントです。親のコンテキストルートはすでにデバイスのデータを解決しているため、このコンポーネントは常に現在のデバイスの詳細データを表示します。

これを app.module.tsentryComponents に追加することでアプリケーションをコンパイルすることができます:


import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouterModule as NgRouterModule } from '@angular/router';
import { UpgradeModule as NgUpgradeModule } from '@angular/upgrade/static';
import { CoreModule, RouterModule, HOOK_ONCE_ROUTE, ViewContext } from '@c8y/ngx-components';
import { UpgradeModule, HybridAppModule, UPGRADE_ROUTES } from '@c8y/ngx-components/upgrade';
import { AssetsNavigatorModule } from '@c8y/ngx-components/assets-navigator';
// ---- 8< added part ----
import { HelloComponent } from './hello.component';
// ---- >8 ----

@NgModule({
  imports: [
    BrowserModule,
    RouterModule.forRoot(),
    NgRouterModule.forRoot([
      ...UPGRADE_ROUTES,
    ], { enableTracing: false, useHash: true }),
    CoreModule.forRoot(),
    AssetsNavigatorModule,
    NgUpgradeModule,
    UpgradeModule
  ],

  // ---- 8< added part ----
  declarations: [HelloComponent],
  entryComponents: [HelloComponent],
  // ---- >8 ----

  providers: [{ 
    provide: HOOK_ONCE_ROUTE,
    useValue: [{
      context: ViewContext.Device,
      path: 'hello',
      component: HelloComponent,
      label: 'hello',
      priority: 100,
      icon: 'rocket'
    }], 
    multi: true
  }]
})
export class AppModule extends HybridAppModule {
  constructor(protected upgrade: NgUpgradeModule) {
    super();
  }
}

npm start でアプリケーションを開始する場合、デバイスの詳細ビューへアクセスすると以下のように表示されます:

カスタムタブ付きのデバイス情報

おめでとうございます。デバイスへタブが追加されました。テナント、ユーザ、アプリケーションの詳細ビューにも同様のことができます。

次は特定の条件が満たされた場合のみこのタブを表示する方法を学びましょう。

(ボーナス)4. 特定の条件下でのみタブを表示する

場合によっては、特定の条件を満たした場合にのみ追加の情報を利用できることがあります。例えば、デバイスに位置情報のフラグメントが関連付けられている場合にのみ位置情報を表示することは意味があります。そのような条件を追加するには、コンテキストルートは Angularのガード概念 を継承します。

ガードを追加するには、単純にルート定義に canActivate プロパティを追加するだけです:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouterModule as NgRouterModule } from '@angular/router';
import { UpgradeModule as NgUpgradeModule } from '@angular/upgrade/static';
import { CoreModule, RouterModule, HOOK_ONCE_ROUTE, ViewContext } from '@c8y/ngx-components';
import { UpgradeModule, HybridAppModule, UPGRADE_ROUTES } from '@c8y/ngx-components/upgrade';
import { AssetsNavigatorModule } from '@c8y/ngx-components/assets-navigator';
import { HelloComponent } from './hello.component';

// ---- 8< added part ----
import { HelloGuard } from './hello.guard';
// ---- >8 ----

@NgModule({
  imports: [
    BrowserModule,
    RouterModule.forRoot(),
    NgRouterModule.forRoot([
      ...UPGRADE_ROUTES,
    ], { enableTracing: false, useHash: true }),
    CoreModule.forRoot(),
    AssetsNavigatorModule,
    NgUpgradeModule,
    UpgradeModule
  ],
  declarations: [HelloComponent],
  entryComponents: [HelloComponent],
  providers: [

    // ---- 8< added part ----
    HelloGuard,
    // ---- >8 ----

    {
    provide: HOOK_ONCE_ROUTE,
    useValue: [{
      context: ViewContext.Device,
      path: 'hello',
      component: HelloComponent,
      label: 'hello',
      priority: 100,
      icon: 'rocket',

      // ---- 8< added part ----
      canActivate: [HelloGuard]
      // ---- >8 ----

    }], 
    multi: true
  }]
})
export class AppModule extends HybridAppModule {
  constructor(protected upgrade: NgUpgradeModule) {
    super();
  }
}

これで、特定の基準をチェックするガードを作成できます。trueに解決されると、タブが表示され、それ以外の場合は表示されません。

デバイスの特定のフラグメントをチェックするガードはこの hello.guard.ts のようになります:

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot } from '@angular/router';
import { Observable } from 'rxjs';

@Injectable()
export class HelloGuard implements CanActivate {

  canActivate(route: ActivatedRouteSnapshot): Observable<boolean> | Promise<boolean> | boolean {
    const contextData = route.data.contextData || route.parent.data.contextData;          // 1.
    const { 'acme_HelloWorld': helloWorldFragment } = contextData;                        // 2.
    return !!helloWorldFragment;
  }
}

上記の数字は以下のように説明できます:

  1. これは、Angularルーターと整合していない唯一の扱いにくい部分です。コンテキストルートでは、CanActivate が2回呼び出されます。1回は親ルートがアクティブになり、1回は子ルートがアクティブになります。最初の呼び出しは、タブを表示する必要があるかどうかを確認し、2番目の呼び出しは、ユーザーがそのタブに移動できるかどうかを確認します。したがって、 ActivatedRouteSnapshotは両方の呼び出しで異なり、2番目のケースでは親から contextDataを解決する必要があります。
  2. acme_HelloWorld フラグメントがコンテキストに設定sあれているかを確認します。

APIに acme_HelloWorld フラグメントを付与してデバイスをポストすると、他のデバイスではなく作成したデバイスのみにHelloタブが表示されるでしょう。

結論

コンテキストルートは、既存のルートをさらに詳細に拡張するのに役立ちます。

同時に、コンテキストは一度のみ解決され、見つからないコンテキストは親によって処理されるため、この概念によりアプリケーションの一貫性がが保たれます。

しかし、コンテキストルートの概念を抽象化し、自身のコンテキストルートを実装するデフォルトの方法は現時点では存在しません。しかし、この概念は Angularのルーティング に基づいているため比較的容易に概念を実装できます。

ダッシュボードにカスタムウィジェットを追加する

バージョン: 10.4.11.0 | パッケージ: @c8y/cli and @c8y/ngx-components

Things Cloudで提供しているウィジェットが要件を満たさない場合、カスタムウィジェットを作成しダッシュボードに追加できます。

典型的なダッシュボードは以下のようになり、様々なウィジェットが表示されます:

ダッシュボード

ここでは COMPONENT_HOOK を用いてダッシュボードにカスタムウィジェットを追加する方法を示しています。

1. サンプルアプリを初期化する

まずはじめに、ダッシュボードを表示するアプリケーションが必要です。このために、c8ycli を使用して新しいコックピットのアプリケーションを作成します:

c8ycli new my-cockpit cockpit

次に、依存関係をインストールします。新しく作成されたフォルダに移動し、npm install を実行してください。

2. ウィジェットコンポーネントを作成する

ウィジェットは通常2つの部分で構成されます:

したがって2つのコンポーネントを作成する必要があります。

はじめに、demo-widget.component.ts を作成します:

import { Component, Input } from '@angular/core';

@Component({
  selector: 'c8y-widget-demo',
  template: `<p class="text">{{config?.text || 'No text'}}</p>`,
  styles: [ `.text { transform: scaleX(-1); font-size: 3em ;}` ]
})
export class WidgetDemo {
  @Input() config;
}

このコンポーネントについては特に言及することはありません。CSSを介して、垂直に鏡写しされたテキストが表示されるのみです。基本的には、他のAngularのコンポーネントでできることを全て行うことができます。

次のように定義されている demo-widget-config.component.ts から設定を渡すために config の入力が必要です:

import { Component, Input } from '@angular/core';

@Component({
  selector: 'c8y-widget-config-demo',
  template: `<div class="form-group">
    <c8y-form-group>
      <label translate>Text</label>
      <textarea style="width:100%" [(ngModel)]="config.text"></textarea>
    </c8y-form-group>
  </div>`
})
export class WidgetConfigDemo {
  @Input() config: any = {};
}

ここでもウィジェットに渡したいシリアル化可能な設定で埋めることのできる config オブジェクトを追加する必要があります。

3. アプリケーションにウィジェットを追加する

ウィジェットを追加するには、COMPONENT_HOOK を使用し entryComponent として作成したコンポーネントを定義する必要があります。

そのためには、app.module.ts に以下を追加します:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouterModule } from '@angular/router';
import { UpgradeModule as NgUpgradeModule } from '@angular/upgrade/static';

// --- 8< changed part ----
import { CoreModule, HOOK_COMPONENT } from '@c8y/ngx-components';
// --- >8 ----

import { UpgradeModule, HybridAppModule, UPGRADE_ROUTES } from '@c8y/ngx-components/upgrade';
import { AssetsNavigatorModule } from '@c8y/ngx-components/assets-navigator';

// --- 8< added part ----
import { WidgetDemo } from './demo-widget.component';
import { WidgetConfigDemo } from './demo-widget-config.component';
// --- >8 ----

@NgModule({
  imports: [
    BrowserModule,
    RouterModule.forRoot([
      ...UPGRADE_ROUTES
    ], { enableTracing: false, useHash: true }),
    CoreModule.forRoot(),
    AssetsNavigatorModule,
    NgUpgradeModule,
    UpgradeModule
  ],

  // --- 8< added part ----
  declarations: [WidgetDemo, WidgetConfigDemo],      // 1.
  entryComponents: [WidgetDemo, WidgetConfigDemo],
  providers: [{
    provide: HOOK_COMPONENT,                         // 2.
    multi: true,
    useValue: {
      id: 'acme.text.widget',                        // 3. 
      label: 'Text widget',
      description: 'Can display a text',
      component: WidgetDemo,                         // 4.
      configComponent: WidgetConfigDemo,
    }
  }],
  // --- >8 ----

})
export class AppModule extends HybridAppModule {
  constructor(protected upgrade: NgUpgradeModule) {
    super();
  }
}

上記の数字の説明は以下の通りです:

  1. エントリーコンポーネントとしてコンポーネントを定義し、このモジュールによってアクセスできるように宣言します。
  2. HOOK_COMPONENT と共にマルチプロバイダーフックを追加します。このフックはアプリケーションによって収集され、設定した値に基づいてウィジェットに追加します。
  3. インベントリに保存されているデータを特定するため、IDはユニークである必要があります。ラベルと詳細記述はタイトルとウィジェットのドロップダウンとして表示されます。
  4. この部分は、すでに定義してあるコンポーネントとウィジェットに関連づけるようフックに指示します。

npm start でアプリケーションを開始すると、ダッシュボードにカスタムウィジェットが表示されているはずです。

ダッシュボードに追加されると、ウィジェットは以下のように表示されます:

カスタムウィジェット