×
Back to book

Custom Tiles in SpringBot

In this article, we will be exploring how easy it is adding custom code into a tile.

It is easy to add custom tiles should you require more fine grain control over your UI elements.

Implementation

Task: Create a custom tile with a chart to display a simple graph of fish counts. Here we will use ChartJS for charts and graphs.

For this example, we must create a page and custom tile (named 'Stats') in our project through the Codebots Build section and ask SpringBot to rebuild the application.

Once we have created our new custom tile, we will be required to interact with the following files to complete this task.

File Name Description
package.json Where we will install our new graph library
stats.tile.component.html HTML file where we define our new chart with graphs
stats.tile.component.ts TypeScript file that accompanies the HTML file, allowing us to define functionality
stats.tile.component.scss SCSS file that contains styling for our new chart
stats.tile.component.module.ts TypeScript file to include our component as a single module

To install ChartJS:

  1. Open a terminal and navigate to clientside
  2. Run npm install --save chart.js and npm install --save-dev @types/chart.js

    @types/chart.js is a type definition file for TypeScript

Now that we have added everything we need, we can now add and display our graph in our tile:

  1. Locate stats.tile.component.html and insert the following code into the protected region:

    Remember to turn on the protected regions

<!-- % protected region % [Add any additional HTML structure here] on begin -->
<div>
    <canvas #myChart></canvas>
</div>
<!-- % protected region % [Add any additional HTML structure here] end -->

The wrapping div element is required as without it ChartJS will not be able to create a chart for us.

  1. Locate stats.tile.component.ts.
  2. Modify the import statements to inside Add any additional imports here protected region:
// % protected region % [Add any additional imports here] off begin
import {ElementRef, ViewChild} from '@angular/core';
import {Chart} from 'chart.js';
import {Store} from '@ngrx/store';
import {
    getFishCollectionModels,
    getFishCollectionState
} from '../../../models/fish/fish.model.selector';
import {Observable} from 'rxjs';
import {FishModel} from '../../../models/fish/fish.model';
import * as modelAction from '../../../models/fish/fish.model.action';
import {QueryOperation} from '../../../lib/services/http/interfaces'; 
import { FishModelState } from 'src/app/models/fish/fish.model.state';
// % protected region % [Add any additional imports here] end
  1. Insert the following code block inside the protected region Add any additional class fields here:
// % protected region % [Add any additional class fields here] on begin
  aliveFishes: Observable<FishModel[]>;
    aliveFishesCount: number = 0;
    aliveFishesId = 'alive-fish';

    deadFishes: Observable<FishModel[]>;
    deadFishesCount: number = 0;
    deadFishesId = 'dead-fish';

    constructor(
        private readonly store: Store<{ model: FishModelState }>,
    ) {

        this.store.dispatch(new modelAction.InitialiseFishCollectionState({
            collectionId: this.aliveFishesId
        }));

        this.aliveFishes = this.store.select(getFishCollectionModels, this.aliveFishesId);

        this.store.dispatch(new modelAction.FetchFishModelsWithQuery({
                queryParams: {
                    pageIndex: 0,
                    pageSize: 1000,
                    where: [
                        [
                            {
                                path: 'fishAlive',
                                operation: QueryOperation.EQUAL,
                                value: 'true'
                            }
                        ]
                    ]

                },
                collectionId: this.aliveFishesId
            },
        ));


        this.store.select(getFishCollectionState, this.aliveFishesId).subscribe(collectionStatus => {
            this.aliveFishesCount = collectionStatus.collectionCount;
            this.updateChart();
        });

        // Get Dead Fish
        this.store.dispatch(new modelAction.InitialiseFishCollectionState({
            collectionId: this.deadFishesId
        }));

        this.aliveFishes = this.store.select(getFishCollectionModels, this.deadFishesId);

        this.store.dispatch(new modelAction.FetchFishModelsWithQuery({
                queryParams: {
                    pageIndex: 0,
                    pageSize: 1000,
                    where: [
                        [
                            {
                                path: 'fishAlive',
                                operation: QueryOperation.EQUAL,
                                value: 'false'
                            }
                        ]
                    ]

                },
                collectionId: this.deadFishesId
            },
        ));


        this.store.select(getFishCollectionState, this.deadFishesId).subscribe(collectionStatus => {
            this.deadFishesCount = collectionStatus.collectionCount;
            this.updateChart();
        });

    }

    updateChart() {
        if (this.chart) {
            this.chart.data.datasets[0].data = [
                this.aliveFishesCount,
                this.deadFishesCount,
            ];
            this.chart.update();
        }
    }



    @ViewChild('canvas')
    canvasEl: ElementRef;

    chart: Chart;

    ngAfterViewInit() {
        const ctx = (this.canvasEl.nativeElement as HTMLCanvasElement).getContext('2d');

        this.chart = new Chart(ctx, {
            // The type of chart we want to create
            type: 'bar',

            // The data for our dataset
            data: {
                labels: ['Alive', 'Dead'],
                datasets: [{
                    label: 'Fish count',
                    backgroundColor: 'rgb(255, 99, 132)',
                    borderColor: 'rgb(255, 99, 132)',
                    data: [this.aliveFishesCount, this.deadFishesCount]
                }]
            },
            // Configuration options go here
            options: {
                title: {
                    text: 'Fish Count',
                    display: true
                },
                scales: {
                    xAxes: [{
                        display: true
                    }],
                    yAxes: [{
                        display: true,
                        ticks: {
                            beginAtZero: true
                        }
                    }],
                }
            }
        });
    }
// % protected region % [Add any additional class fields here] end
  1. Open the file stats.tile.component.html found in clientside/src/app/tiles/custom/stats/stats.tile.component.html.

  2. Find the protected region [Add any additional HTML structure here], turn it on and paste the following into it.

<div>
    <canvas #canvas></canvas>
</div>