Welcome to the 4th round of the web development series.
In the last part you have integrated the @c8y/client from the Cumulocity Web SDK to communicate with Cumulocity’s API. In particular, you have learned how to query data from the Inventory using the InventoryService
. Among other, the Inventory stores your device data. To have some device data available in the Inventory of your Cumulocity instance, you have created a temperature simulator using the simulator self-service feature in the Device Management application. The temperature simulator sends continuously temperature measurements. Using the MeasurementService
from the @c8y/client
you have queried the current temperature measurement. To always get the latest measurement from the simulator and display it in the UI you have integrated the Realtime
service into your component. With these changes you could remove all previously mocked data from the device-info
module and achieve a more dynamic behavior of the component.
In the 4th part of the web development series, the device-info
component will be converted into a Cumulocity widget, which can then be used dynamically on Cumulocity dashboards.
Add dashboarding to your application
Originally, dashboards are part of the Cockpit application. But you can integrate the dashboard feature in any of your custom applications, as the necessary component is part of the @c8y/ngx-components
package. In this section, you will update your application and add a dashboard to it. You can continue with your project or use part-03 from github. If you are struggling with part 4, you can find the final application in the corresponding github project.
In the src
directory create a new folder called named-dashboard
. This folder will be home to your dashboard module. Create a new file for the template named named-dashboard.component.html
.
named-dashboard.component.html
<c8y-title>Named dashboard</c8y-title>
<c8y-context-dashboard [name]="'My named dashboard'" [canDelete]="false"></c8y-context-dashboard>
The template consists of a c8y-title
element to specify the title in the header. In addition, it uses the Cumulocity ContextDashboard component c8y-context-dashboard from @c8y/ngx-components
to instantiate a new dashboard. In this case, the dashboard will be a named dashboard. This means the context of your dashboard is defined by its name and therefore will be identified by its name: My named dashboard
. To learn more about context dashboards have a look at the tutorial application of Cumulocity. The tutorial application provides some examples for Cumulocity dashboards.
Important: When you want to see the possibilities and implementation details of the Web SDK you should try the tutorial application. You can install it by running
c8ycli new <<your-app-name>> tutorial
.
Create the corresponding component and name the file named-dashboard.component.ts
. The purpose of the component is to serve the previously created template:
named-dashboard.component.ts
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'named-dashboard-component',
templateUrl: './named-dashboard.component.html',
})
export class NamedDashboardComponent implements OnInit {
constructor() {}
ngOnInit() {}
}
You will need a navigation factory to add a navigation entry to the left side menu to navigate to your dashboard. Create a new navigation factory called: named-dashboard.factory.ts
.
named-dashboard.factory.ts
import { Injectable } from '@angular/core';
import { NavigatorNode, NavigatorNodeFactory } from '@c8y/ngx-components';
@Injectable()
export class NamedDashboardNavigationFactory implements NavigatorNodeFactory {
get() {
return new NavigatorNode({
label: 'Dashboard',
icon: 'robot',
path: 'named-dashboard',
priority: 50,
});
}
}
In the NamedDashboardNavigationFactory
you will register a new NavigatorNode
, which forwards the user to your dashboard component via the path named-dashboard
.
Finally, you need to create the module for your new dashboard component. Create the additional file named-dashboard.module.ts
in your named-dashboard
folder:
named-dashboard.module.ts
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { CoreModule, HOOK_NAVIGATOR_NODES } from '@c8y/ngx-components';
import { ContextDashboardModule } from '@c8y/ngx-components/context-dashboard';
import { NamedDashboardComponent } from './named-dashboard.component';
import { NamedDashboardNavigationFactory } from './named-dashboard.factory';
const routes: Routes = [
{
path: '',
redirectTo: 'named-dashboard',
pathMatch: 'full',
},
{
path: 'named-dashboard',
component: NamedDashboardComponent,
},
];
@NgModule({
imports: [
ContextDashboardModule.config(),
CommonModule,
RouterModule.forChild(routes),
CoreModule,
ContextDashboardModule,
],
exports: [],
declarations: [NamedDashboardComponent],
providers: [
{ provide: HOOK_NAVIGATOR_NODES, useClass: NamedDashboardNavigationFactory, multi: true },
],
})
export class NamedDashboardModule {}
The module imports the ContextDashboardModule
from @c8y/ngx-components
to provide the dashboard feature for your application. Furthermore, the NamedDashboardNavigationFactory
is registered and provided using the HOOK_NAVIGATOR_NODES
. The path named-dashboard
defined in the NamedDashboardNavigationFactory
will be connected to the NamedDashboardComponent
by using the RouterModule
.
Finally, update the AppModule
and import your NamedDashboardModule
:
import { NamedDashboardModule } from './src/named-dashboard/named-dashboard.module';
@NgModule({
imports: [
...
NamedDashboardModule,
],
bootstrap: [BootstrapComponent],
})
export class AppModule {}
If you start your application, you will see a new navigation entry in the left side navigation to access the named dashboard component
For now, you can’t add any widgets to the dashboard, because the list of widgets is empty. You are going to change this by converting the device-info
component to a widget.
Convert the device-info
component into a c8y widget
Let’s update the DeviceInfoComponent
component in the device-info
directory. First, remove the device-info.factory.ts
file from the project. The DeviceInfoNavigationFactory
isn’t needed anymore, as the component should be used on the dashboard in the future.
Update the device-info.module.ts
. Remove the DeviceInfoNavigationFactory
and the routes (including the RouterModule
and the corresponding imports) from it. Now, add the widget configuration to the module. The widget configuration is provided using the hook HOOK_COMPONENTS
from @c8y/ngx-components
.
device-info.module.ts
...
import { CoreModule, DynamicComponentDefinition, HOOK_COMPONENTS } from '@c8y/ngx-components';
import { ContextWidgetConfig } from '@c8y/ngx-components/context-dashboard';
...
@NgModule({
imports: [CoreModule],
exports: [],
declarations: [DeviceInfoComponent],
providers: [
{
provide: HOOK_COMPONENTS,
multi: true,
useValue: [
{
id: 'device-info.widget',
label: 'Device Info Widget',
description: 'This is a sample widget',
component: DeviceInfoComponent,
data: {
settings: {
noNewWidgets: false,
ng1: {
options: {
noDeviceTarget: false,
groupsSelectable: false,
},
},
},
} as ContextWidgetConfig,
},
] as DynamicComponentDefinition[],
},
],
})
export class DeviceInfoModule {}
In the configuration of the widget, you define following properties:
-
id
- unique identifier for you widget -
label
- used as the title in the widget catalog -
description
- used as the description in the widget catalog -
component
- the component, which should be used for the widget. You define theDeviceInfoComponent
as the relevant component.
In the data property, you can define some basic configuration for your widget
-
noDeviceTarget
- set to false if a you want to enable the device selector for your widget -
groupsSelectable
- set to true if groups should be selectable as well
In case of the device-info
widget, you will enable device selection, but disable group selection.
Next you need to update the DeviceInfoComponent
to correctly receive the configuration, if the component is added as a widget to the dashboard. Remove the hardcoded device id from the component. Instead add a new variable called config
for the configuration of your widget, which is marked as Input()
in your component. This variable config
is automatically set, when the configuration for your widget is completed and the widget has been added to the dashboard.
device-info.component.ts
import { Component, Input, OnInit } from '@angular/core';
...
export class DeviceInfoComponent implements OnInit {
@Input() config: { device: { id: string; name: string } };
...
private async initDeviceDetails() {
this.deviceDetails = await this.deviceInfoService.getDeviceDetails(this.config.device.id);
}
private subscribeForTemperatureMeasurements() {
this.deviceInfoService.temperatureMeasurement$.subscribe(
(temperatureMeasurement) => (this.tempteratureMeasurement = temperatureMeasurement)
);
this.deviceInfoService.subscribeForTemperatureMeasurements(this.config.device.id);
}
Instead of using the hardcoded device id for the method calls this.deviceInfoService.getDeviceDetails()
and this.deviceInfoService.subscribeForTemperatureMeasurements()
, you will use the id of the device, which has been selected as an input for your widget: this.config.device.id
.
Lastly, you will update the template for your device-info
widget. Simply remove the card
class from the root <div>
element:
device-info.component.html
<div class="p-l-16 p-r-16" *ngIf="deviceDetails">
...
</div>
The card
class is removed, because if your device-info
widget is added to a dashboard, it will automatically be wrapped in a card
element.
Run your application locally and add your newly created widget to the named dashboard
. Select your temperature simulator as an input device for your widget.
Conclusion
Great, you have successfully converted the device-info
component into a widget, which now can be used on Cumulocity dashboards. You can view the full code for part 4 in the corresponding github project.
In part 5 we will have a look at the newly introduced Micro-Frontend framework and learn how the device-info
widget can be provided as UI plugin to be used in the Micro-Frontend framework. Check out this article to get a first introduction.
Top comments (0)