Cumulocity Web Development Tutorial - Part 2: Create your first component
Tags: iot, tutorial, beginners, component
Published: 2023-01-09 14:39:48

---
title: Cumulocity Web Development Tutorial - Part 2: Create your first component
published: true
date: 2022-11-25 07:46:48 UTC
tags: iot, tutorial, beginner, component
canonical_url: https://tech.forums.softwareag.com/t/cumulocity-web-development-tutorial-part-2-create-your-first-component/265762/1
---

 

## Welcome back!

![welcome-back](https://aws1.discourse-cdn.com/techcommunity/original/3X/8/7/870eb7a788383cae4c123c62fe0d4b7358778f10.gif)

Welcome back to the second part of the Cumulocity Web Development Tutorial series. In the [first part](https://tech.forums.softwareag.com/t/cumulocity-web-development-tutorial-part-1-start-your-journey/259613), you have started to set up your development environment by installing [Node.js](https://nodejs.org/en/docs/) and the [Cumulocity Web Developer command line tools (`@c8y/cli`)](https://www.npmjs.com/package/@c8y/cli). Using these tools, you have created your first application, which doesn’t have any content yet. This is going to change.

In the second part you will create your first component and add a new entry in the navigation menu to access this component. The component will be fairly simple. It will display some hardcoded device details and a random temperature measurement.

![image](https://aws1.discourse-cdn.com/techcommunity/original/3X/b/a/ba439fd4d3b6355fe636ae82f862420b5433f9c3.png)

Even though the component is simple, you will learn quite some new concepts and best practices. You will learn how you can hook into built-in Cumulocity components, such as the navigation menu to extend it with custom entries. Furthermore, you will get to know [Cumulocity’s styleguide](http://styleguide.cumulocity.com/#/) to build UIs, which follow Cumulocity’s look and feel. In addition, there will be an introduction to content projection in the context of Cumulocity.

## Starting simple

The component you will implement, registers a new entry in the navigation menu on the left side. Clicking on the navigation entry will open a detail page, which displays general information and temperature measurements for a device. For now, the device and its data will be mocked. In the next part of this tutorial series this is going to change. You will display information for an actual device, which is available in your Cumulocity instance.

Let’s start coding. You can find the source code for this article in the corresponding git repository in the [part 02 directory](https://github.com/SoftwareAG/cumulocity-web-development-tutorial/tree/main/part-02/).

![cat-coding](https://aws1.discourse-cdn.com/techcommunity/original/3X/c/c/cc4728ecb2a9b775f966420d3276442416fe5440.gif)

You can extend the existing application from the [first part](https://tech.forums.softwareag.com/t/cumulocity-web-development-tutorial-part-1-start-your-journey/259613/4) or create a new application from scratch. Create a new directory in your project called `src`. The `src` directory will be the location, where you will place your modules, components and services. Inside the `src` folder create another folder called `device-info`. This will be the home to your first module.

### Create the model

As you are a exemplary Typescript and Angular developer, you are well aware of [separation of concerns (MVC)](https://angular.io/guide/styleguide#single-responsibility) and providing type definitions. This means, the code will be separated based on the responsibility. Let’s define the model of our component first. Create a new file inside the `device-info` directory, called `device-info.model.ts`. It will consist of two interfaces:

**device-info.model.ts**

```
export interface DeviceDetails {
  name: string;
  type: string;
}

export interface TemperatureMeasuerement {
  value: number;
  unit: string;
}

```

The `DeviceDetails` interface is used to describe the structure of a device. For now, a device is described by its name and type. As the device is capable of sending temperature measurements, there is a second interface to define the structure of such a temperature measurement: `TemperatureMeasurement`. A temperature measurement consists of a value and a unit.

### Create some data

These interfaces will be used in the service of the device-info module to provide data. The service is responsible for creating device data and temperature measurements, which later will be picked up by the component. Next to the `device-info.model.ts` file create another file called `device-info.service.ts`.

**device-info.service.ts**

```
import { Injectable, resolveForwardRef } from '@angular/core';
import { Subject } from 'rxjs';
import { DeviceDetails, TemperatureMeasuerement } from './device-info.model';

@Injectable()
export class DeviceInfoService {
  temperatureMeasurement$: Subject<TemperatureMeasuerement> =
    new Subject<TemperatureMeasuerement>();

  constructor() {}

  async getDeviceDetails(): Promise<DeviceDetails> {
    return new Promise<DeviceDetails>((resolve) =>
      resolve({ name: 'My test device', type: 'c8y_TestType' })
    );
  }

  subscribeForTemperatureMeasurements(): void {
    // publish latest measurement
    this.temperatureMeasurement$.next({ value: 10, unit: '°C' });

    // push random temperature every 10 seconds
    setInterval(
      () => this.temperatureMeasurement$.next({ value: this.getRandomInt(8, 15), unit: '°C' }),
      10000
    );
  }

  private getRandomInt(min, max) {
    min = Math.ceil(min);
    max = Math.floor(max);
    return Math.floor(Math.random() * (max - min) + min);
  }
}

```

Let’s have a look at the service. The service will be the foundation for the third part of this tutorial series, where you will query data using Cumulocity’s API. In this example, the data is mocked. The service contains two public methods:

```
async getDeviceDetails(): Promise<DeviceDetails> {
    return new Promise<DeviceDetails>((resolve) =>
      resolve({ name: 'My test device', type: 'c8y_TestType' })
    );
}

```

The `getDeviceDetails()` method uses the `DeviceDetails` interface, which you have created previously to return a mocked device as Promise. Furthermore, the service provides a [Subject](https://rxjs.dev/guide/subject) to which a component can subscribe to. The subject is used to publish incoming temperature measurements:

```
 temperatureMeasurement$: Subject<TemperatureMeasuerement> = new Subject<TemperatureMeasuerement>();

```

The measurements are created by the second method `subscribeForTemperatureMeasurements()`.

```
subscribeForTemperatureMeasurements(): void {
    // publish latest measurement
    this.temperatureMeasurement$.next({ value: 10, unit: '°C' });

    // push random temperature every 10 seconds
    setInterval(
      () => this.temperatureMeasurement$.next({ value: this.getRandomInt(8, 15), unit: '°C' }),
      10000
    );
}

```

The method initially publishes the latest mocked temperature measurement. Afterwards, an interval of 10 seconds is specified to continuously publish random temperature measurements.

### Create the component

To display this data on a page, a component is required, which orchestrates the communication between template and service. The component is created in the `device-info` directory and is called `device-info.component.ts`. It will use the service, which you created previously, to receive data about the device and its temperature measurements. This data is displayed in the corresponding template, which you will create later.

**device-info.component.ts**

```
import { Component, OnInit } from '@angular/core';
import { DeviceDetails, TemperatureMeasuerement } from './device-info.model';
import { DeviceInfoService } from './device-info.service';

@Component({
  selector: 'c8y-device-info',
  templateUrl: 'device-info.component.html',
  providers: [DeviceInfoService],
})
export class DeviceInfoComponent implements OnInit {
  tempteratureMeasurement: TemperatureMeasuerement;

  deviceDetails: DeviceDetails;

  constructor(private deviceInfoService: DeviceInfoService) {}

  ngOnInit() {
    this.initDeviceDetails();
    this.subscribeForTemperatureMeasurements();
  }

  private async initDeviceDetails() {
    this.deviceDetails = await this.deviceInfoService.getDeviceDetails();
  }

  private subscribeForTemperatureMeasurements() {
    this.deviceInfoService.temperatureMeasurement$.subscribe(
      (temperatureMeasurement) => (this.tempteratureMeasurement = temperatureMeasurement)
    );

    this.deviceInfoService.subscribeForTemperatureMeasurements();
  }
}

```

In the `@Component` annotation, the component provides the `DeviceInfoService`, so it can be used by the component. The component has two variables defined:

```
  tempteratureMeasurement: TemperatureMeasuerement;

  deviceDetails: DeviceDetails;

```

Both variables use the interfaces, which have been defined in the corresponding [model file](https://github.com/SoftwareAG/cumulocity-web-development-tutorial/blob/main/part-02/my-c8y-application/src/device-info/device-info.model.ts). They will be used by the template to display relevant data to the user. By implementing the [OnInit](https://angular.io/guide/lifecycle-hooks) interface the component registers the lifecycle callback `ngOnInit` to be notified once the component has been initialized. Once `ngOnInit` is triggered, the component uses the `DeviceInfoService` to initialize the `deviceDetails` and to subscribe for temperature measurements.

```
private subscribeForTemperatureMeasurements() {
    this.deviceInfoService.temperatureMeasurement$.subscribe(
      (temperatureMeasurement) => (this.tempteratureMeasurement = temperatureMeasurement)
    );

    this.deviceInfoService.subscribeForTemperatureMeasurements();
  }

```

The subscription is done by subscribing to the Subject exposed by the service. At the same time, the component triggers the publication of temperature measurements by executing the method `subscribeForTemperatureMeasurements()` of the service.

### Display information

The last building block is to display the device information. This is done in the template, which will be created inside the `device-info` directory. Name the file `device-info.component.html`:

**device-info.template.html**

```
<c8y-title>{{ 'Device info' | translate }}</c8y-title>

<div class="card col-sm-3 p-8" *ngIf="deviceDetails">
  <p class="legend form-block">{{ 'Device details' | translate }}</p>
  <div class="form-group">
    <label>{{ 'Name' | translate }}</label>
    <p class="form-control-static">{{ deviceDetails.name }}</p>
  </div>
  <div class="form-group">
    <label>{{ 'Type' | translate }}</label>
    <p class="form-control-static">{{ deviceDetails.type }}</p>
  </div>
  <p class="legend form-block">{{ 'Measurements' | translate }}</p>
  <div class="form-group">
    <label>{{ 'Temperature' | translate }}</label>
    <p class="form-control-static">
      {{ tempteratureMeasurement?.value ?? '-' }} {{ tempteratureMeasurement?.unit }}
    </p>
  </div>
</div>

```

You can display information about the device and the temperature by using the variables (`deviceDetails` and `temperatureMeasurement`) of the corresponding component in the template. [Interpolation](https://angular.io/guide/interpolation) allows to display the specific values of the two variables.

You might be wondering where some of the CSS classes are coming from, e.g. `card` or `p-8`. Cumulocity provides a [Styleguide](http://styleguide.cumulocity.com/#/), which is publicly available:

[![image](https://aws1.discourse-cdn.com/techcommunity/optimized/3X/7/6/76da0fce60f21ef0026af36086661b8bc55c49d7_2_689x411.png)](https://aws1.discourse-cdn.com/techcommunity/original/3X/7/6/76da0fce60f21ef0026af36086661b8bc55c49d7.png "image")

The styleguide for Cumulocity IoT collects and shares information on how you can create user interfaces by following Cumulocity’s UX/UI principles and best practices. In the section `Components` you can find an overview of existing components as part of the `ngx-components` package and how to use these in your template. It describes what [type of buttons](https://styleguide.cumulocity.com/#/components/buttons) exist and how to use these. Same with the [card component](https://styleguide.cumulocity.com/#/components/cards), which is used in the template.  
The `Utilities` sections describes CSS utilities, which you can reuse in your application and plugins. These can be for example predefined classes to set [the padding (e.g `p-8`) or the margin](https://styleguide.cumulocity.com/#/utilities/margin-and-padding).

> **Tip:** As you can see the [Styleguide](http://styleguide.cumulocity.com/#/) is really helpful and full of information. Take your time and have a look at it to get a better understanding of existing principles and components in Cumulocity IoT.

The template for the device-info component uses a built-in Cumulocity component:

```
<c8y-title>{{ 'Device info' | translate }}</c8y-title>

```

The `c8y-title` component is part of the [ngx-components package](http://resources.cumulocity.com/documentation/websdk/ngx-components/). It is used for [content projection](https://angular.io/guide/content-projection). For example, you can configure the title of a page by setting a `<c8y-title>` in any other component. The content of the tag is then projected to an outlet component, which is placed in the header bar.

[![image](https://aws1.discourse-cdn.com/techcommunity/original/3X/5/4/545907f1f959bc210cb257e96e2679738f24cd98.png)](https://aws1.discourse-cdn.com/techcommunity/original/3X/5/4/545907f1f959bc210cb257e96e2679738f24cd98.png "image")

There are additional components like the `c8y-action-bar-item`, which can be used for content projection in Cumulocity. Have a look at the [documentation](http://resources.cumulocity.com/documentation/websdk/ngx-components/) for more information.

### Navigating to the component

To actually see the component, which you have just created, you need to register it in Cumulocity’s navigation menu. Create a new file called `device-info.factory.ts` in the `device-info` directory.

**device-info.factory.ts**

```
import { Injectable } from '@angular/core';
import { NavigatorNode, NavigatorNodeFactory } from '@c8y/ngx-components';

@Injectable()
export class DeviceInfoNavigationFactory implements NavigatorNodeFactory {
  get() {
    return new NavigatorNode({
      label: 'Device Info',
      icon: 'robot',
      path: 'device-info',
      priority: 100,
    });
  }
}

```

The `DeviceInfoNavigationFactory` is annotated as `@Injectable()` and implements the `NavigatorNodeFactory` interface. The interface asks you to override the `get()` method, which should return a `NavigatorNode`.

```
new NavigatorNode({
      label: 'Device Info',
      icon: 'robot',
      path: 'device-info',
      priority: 100,
    });

```

The `NavigatorNode` defines a label and an icon. These are later displayed in the menu:

![image](https://aws1.discourse-cdn.com/techcommunity/original/3X/b/5/b52793876da0dfb3f3086d7508c8abea346abc4d.png)

The priority specifies at which position the new navigation entry should be shown within the navigation menu.

### Bring everything together in a module

To bring everything together, you need to create a module for your `device-info` component in the corresponding directory called `device-info.module.ts`.

**device-info.module.ts**

```
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { CoreModule, HOOK_NAVIGATOR_NODES } from '@c8y/ngx-components';

import { DeviceInfoComponent } from './device-info.component';
import { DeviceInfoNavigationFactory } from './device-info.factory';

const routes: Routes = [
  {
    path: '',
    redirectTo: 'device-info',
    pathMatch: 'full',
  },
  {
    path: 'device-info',
    component: DeviceInfoComponent,
  },
];

@NgModule({
  imports: [RouterModule.forChild(routes), CoreModule],
  exports: [],
  declarations: [DeviceInfoComponent],
  providers: [
    { provide: HOOK_NAVIGATOR_NODES, useClass: DeviceInfoNavigationFactory, multi: true },
  ],
})
export class DeviceInfoModule {}

```

First, you register the `DeviceInfoComponent` for the path `device-info` in the `routes` variable. If this path is called the application will load the corresponding `DeviceInfoComponent`. The routes are added to the application via the `RouterModule` as part of the imports statement of the module. The `CoreModule` is imported to access components from Cumulocity’s core, for example the `<c8y-title>` component.

Furthermore, the navigation factory, which you have created previously needs to be registered as well. For Cumulocity to recognize the navigation factory, you have to provide it with the special hook called `HOOK_NAVIGATOR_NODES` in the `providers` statement. Besides the `HOOK_NAVIGATOR_NODES` hook there are a couple of additional hooks, which will be covered later in this tutorial series.

> **Tip:** You can read about the various hooks and the underlying concept in the [ngx-components documentation](http://resources.cumulocity.com/documentation/websdk/ngx-components/)

### Run your application

Before you can run your application, you must import your newly created module in the app module (`app.module.ts`). Add the `DeviceInfoModule` to list of imported modules of `AppModules`:

```
import { NgModule } from '@angular/core';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterModule as ngRouterModule } from '@angular/router';
import { CoreModule, BootstrapComponent, RouterModule } from '@c8y/ngx-components';
import { DeviceInfoModule } from './src/device-info/device-info.module';

@NgModule({
  imports: [
    BrowserAnimationsModule,
    RouterModule.forRoot(),
    ngRouterModule.forRoot([], { enableTracing: false, useHash: true }),
    CoreModule.forRoot(),
    DeviceInfoModule,
  ],
  bootstrap: [BootstrapComponent],
})
export class AppModule {}

```

Now you can run the application locally by executing the command

```
npx c8ycli server -u <<C8Y-URL>>

```

as learned in the first part of this tutorial series. Another approach is to update the start script in [package.json](https://github.com/SoftwareAG/cumulocity-web-development-tutorial/blob/main/part-02/my-c8y-application/package.json) and replace `{{c8y-instance}}` with the URL of your Cumulocity environment. To execute the start script, you must run this command:

```
npm run start

```

Your application should now have a little bit more content in it:

[![image](https://aws1.discourse-cdn.com/techcommunity/optimized/3X/d/2/d21fe4e694f764fdbcd6b708949f3bd218f207f6_2_690x488.png)](https://aws1.discourse-cdn.com/techcommunity/original/3X/d/2/d21fe4e694f764fdbcd6b708949f3bd218f207f6.png "image")

## Conclusion and what’s next

![information-overload](https://aws1.discourse-cdn.com/techcommunity/original/3X/7/c/7c2de32a191cb71b213d1e0ab49df16fd6e8ffe4.gif)

This was quite a lot of information to take in. Take your time to process it. The component, which you have implemented in this article, will be the foundation for the next parts of the tutorial series. In the next article, you will learn how to query data from Cumulocity using its API and the Web SDK. Furthermore, you will convert the `device-info` from a plain plugin to a real Cumulocity widget to be used on Cumulocity dashboards.

Make sure to not miss the next article, which is scheduled to be released in about two weeks.

[Read full topic](https://tech.forums.softwareag.com/t/cumulocity-web-development-tutorial-part-2-create-your-first-component/265762/1)