1
\$\begingroup\$

I've written this plotter for Ionic in Typescript to plot data that I push to a Firebase list. I'm new to Typescript so I'm interested in all kinds of feedback, especially on the style.

First I've created a database provider that I used to retrieve the data for the charts from Firebase:

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

import { AngularFireDatabase } from 'angularfire2/database';

// Provides read and write database methods
@Injectable()
export class MyProjectDatabase {

    userID;
    deviceID;

    constructor(
        private afDB: AngularFireDatabase,
    ) {
    }

    public connect(userID) {
        this.userID = userID;
        this.read(`/user/${this.userID}/linked_device`).then( (val) => {
            this.deviceID = val;
        });
    }

    public disconnect() {
        this.userID = "";
        this.deviceID = "";
    }

    // In a regular function:
    // fun () {
    //     db.read(path).then( (val) => {
    //         this.val = val;
    //     });
    // }
    //
    // In an async function:
    // async fun() {
    //     val = await db.read(path);
    // }
    public async read(path: string, query?) {
        let snapshot;
        if (query) {
            snapshot = await this.afDB.list(path, query).query.once("value");
        } else {
            snapshot = await this.afDB.list(path).query.once("value");
        }
        return snapshot.val();
    }

    public write(path: string, value) {
        this.afDB.object(path).set(value);
    }

    public remove(path: string, query?) {
        if (query) {
            this.afDB.list(path, query).remove();
        } else {
            this.afDB.list(path).remove();
        }
    }

    // Returns the values of the list in an array:
    // In a regular function:
    // fun() {
    //     readList(path).then( values => {
    //         values.forEach( value => {
    //             ...
    //         });
    //     });
    // }
    // 
    // In an async function:
    // async fun() {
    //     let values = await readList(path);
    //     values.forEach( value => {
    //         ...
    //     });
    // }
    public async readList(path: string, query?) {
        let snapshot;
        if (query) {
            snapshot = await this.afDB.list(path, query).query.once("value");
        } else {
            snapshot = await this.afDB.list(path).query.once("value");
        }
        let obj = snapshot.val();
        if (obj) {
            return Object.keys(obj).map(key => { return obj[key] });
        } else {
            return [];
        }
    }

    public addToList(path: string, value) {
        this.afDB.list(path).push(value);
    }

    // Returns all entries of the list that match the value in an object { key: value }
    // In a regular function:
    // fun() {
    //     findInList(path, value).then( match => {
    //         for (let key in match) {
    //             console.log(match[key]);
    //         }
    //     });
    // }
    //
    // In an async function:
    // async fun() {
    //     let match = await findInList(path, value);
    //     for (let key in match) {
    //         console.log(match[key]);
    //     }
    // }
    public async findInList(path: string, value) {
        let snapshot = await this.afDB.list(path, ref => ref.orderByValue().equalTo(value)).query.once("value");
        return snapshot.val();
    }

    // Remove all entries in the list that match the value
    public removeFromList(path: string, value) {
        let list = this.afDB.list(path);
        list.query.once("value").then( snapshot => {
            let snapshotValues = snapshot.val();
            for (let key in snapshotValues) {
                console.log(`${snapshotValues[key]} == ${value}`);
                if (snapshotValues[key] == value) {
                    list.remove(key);
                }
            }
        });
    }

}

When the query parameter is optional, is there a better way to check it rather than doing an if(query)?

Then I use this provider in my charts. I have an abstract chart that I use as a template:

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

import { MyProjectDatabase } from '../../../providers/my-project-database';

import { Chart } from 'chart.js';

export abstract class MyProjectChart {
    abstract chartCanvas: ElementRef;
    abstract numberOfMeasurements: number;
    abstract dataPath: string;
    abstract chartStyle: object; 

    private chart: Chart;

    constructor(
        private db: MyProjectDatabase,
    ) {
    }

    public init() {
        this.chart = new Chart(<HTMLCanvasElement>this.chartCanvas.nativeElement, this.chartStyle);
    }

    public update() {
        const componentPath = `/device/${this.db.deviceID}/component`;
        this.db.readList(`${componentPath}/${this.dataPath}`, 
            ref => ref.limitToLast(this.numberOfMeasurements))
            .then( data => {
                data.forEach((item, index) => {
                    this.chart.data.datasets[0].data[index] = item.value;
                    this.chart.data.labels[index] = item.timestamp;
                });
                this.chart.update();
            });
    }

}

An example of a chart would be:

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

import { MyProjectDatabase } from '../../../providers/my-project-database';

import { MyProjectChart } from './my-project-chart'

@Component({
    selector: 'light-chart',
    template: `
            <div style="margin-left:auto; margin-right:auto; position: relative; height:50vh; width:80vw">
                <canvas #chartCanvas></canvas>
            </div>`
})
export class LightChart extends MyProjectChart {
    @ViewChild('chartCanvas') chartCanvas: ElementRef;
    numberOfMeasurements: number = 10;
    dataPath: string = 'lux/data';

    chartStyle: object = {
        type: 'line',
        data: {
            labels: new Array(this.numberOfMeasurements).fill("00:00:00"),
            datasets: [
                {
                    label: "Light Intensity",
                    fill: true,
                    lineTension: 0.5,
                    backgroundColor: "rgb(48,151,127)",
                    borderColor: "rgb(48,151,127,0.8)",
                    borderCapStyle: 'butt',
                    borderDash: [],
                    borderDashOffset: 0.0,
                    borderJoinStyle: 'miter',
                    pointBorderColor: "rgb(48,151,127)",
                    pointBackgroundColor: "rgba(48,151,127)",
                    pointBorderWidth: 3,
                    pointHoverRadius: 5,
                    pointHoverBackgroundColor: "rgb(48,151,127)",
                    pointHoverBorderColor: "rgba(48,151,127)",
                    pointHoverBorderWidth: 2,
                    pointRadius: 3,
                    pointHitRadius: 10,
                    spanGaps: false,
                    data: new Array(this.numberOfMeasurements).fill(10),
                }
            ]
        },
        options: {
            responsive: true,
          maintainAspectRatio: false,
          legend: {
            display: false
          },
          scales: {
              yAxes: [{
                ticks: { min: 0, max: 30, display: false },
                gridLines: {
                  display: false,
                  drawBorder: false
                }
              }],
              xAxes: [{
                gridLines: {
                  display: false,
                  drawBorder: false
                }
              }]
          }
        }
    }

    constructor (
        db: MyProjectDatabase
    ) {
        super(db);
    }

}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.