Getting started with TsLint and tslint-consistent-codestyle in Angular 5+ projects, Part 1: Visual Studio Code

 

Introduction

This article will show you how to integrate TsLint and the consistent codestyle extension to your Angular solution in order to ensure consistent coding standards across your code base.

In this tutorial, Angular 5 and Visual Studio Code will be used.

Step 1

After setting  your angular app, download TsLint for Visual studio code : https://marketplace.visualstudio.com/items?itemName=eg2.tslint

Step 2

Install the tslint-consistent-codestyle npm package. This package adds many useful coding rules not available with the native TsLint, such as naming-convention which allows you to control the way a variable, a function or really anything will be declared.

npm install --save-dev tslint-consistent-codestyle

Step 3

Replace the content of tslint.json by this content (you can edit these rules, they are only just a suggestion):

{
  "rulesDirectory": [
    "node_modules/codelyzer",
    "tslint-consistent-codestyle"
  ],
  "rules": {
    "arrow-return-shorthand": true,
    "callable-types": true,
    "class-name": true,
    "comment-format": [
      true,
      "check-space"
    ],
    "curly": true,
    "deprecation": {
      "severity": "warn"
    },
    "eofline": true,
    "forin": true,
    "import-blacklist": [
      true,
      "rxjs",
      "rxjs/Rx"
    ],
    "import-spacing": true,
    "indent": [
      true,
      "spaces"
    ],
    "interface-over-type-literal": true,
    "label-position": true,
    "max-line-length": [
      true,
      140
    ],
    "member-access": true,
    "member-ordering": [
      true,
      {
        "order": [
          "static-field",
          "instance-field",
          "static-method",
          "instance-method"
        ]
      }
    ],
    "no-arg": true,
    "no-bitwise": true,
    "no-console": [
      true,
      "debug",
      "info",
      "time",
      "timeEnd",
      "trace"
    ],
    "no-construct": true,
    "no-debugger": true,
    "no-duplicate-super": true,
    "no-empty": false,
    "no-empty-interface": true,
    "no-eval": true,
    "no-inferrable-types": [
      true,
      "ignore-params"
    ],
    "no-misused-new": true,
    "no-non-null-assertion": true,
    "no-shadowed-variable": true,
    "no-string-literal": false,
    "no-string-throw": true,
    "no-switch-case-fall-through": true,
    "no-trailing-whitespace": true,
    "no-unnecessary-initializer": true,
    "no-unused-expression": true,
    "no-use-before-declare": true,
    "no-var-keyword": true,
    "object-literal-sort-keys": false,
    "one-line": [
      true,
      "check-open-brace",
      "check-catch",
      "check-else",
      "check-whitespace"
    ],
    "prefer-const": true,
    "quotemark": [
      true,
      "single"
    ],
    "radix": true,
    "semicolon": [
      true,
      "always"
    ],
    "triple-equals": [
      true,
      "allow-null-check"
    ],
    "typedef-whitespace": [
      true,
      {
        "call-signature": "nospace",
        "index-signature": "nospace",
        "parameter": "nospace",
        "property-declaration": "nospace",
        "variable-declaration": "nospace"
      }
    ],
    "unified-signatures": true,
    "variable-name": false,
    "whitespace": [
      true,
      "check-branch",
      "check-decl",
      "check-operator",
      "check-separator",
      "check-type"
    ],
    "directive-selector": [
      true,
      "attribute",
      "app",
      "camelCase"
    ],
    "component-selector": [
      true,
      "element",
      "app",
      "kebab-case"
    ],
    "no-output-on-prefix": true,
    "use-input-property-decorator": true,
    "use-output-property-decorator": true,
    "use-host-property-decorator": true,
    "no-input-rename": true,
    "no-output-rename": true,
    "use-life-cycle-interface": true,
    "use-pipe-transform-interface": true,
    "component-class-suffix": true,
    "directive-class-suffix": true,
    "ordered-imports": [
      true,
      {
        "import-sources-order": "any",
        "named-imports-order": "case-insensitive"
      }
    ],
    "naming-convention": [
      true,
      {"type": "default", "format": "camelCase", "leadingUnderscore": "forbid", "trailingUnderscore": "forbid"},
      {"type": "method", "modifiers": "public", "format": "camelCase"},
      {"type": "member", "modifiers": "private", "leadingUnderscore": "require"},
      {"type": "type", "format": "PascalCase"}
    ],
    "typedef": [
      true,
      "call-signature",
      "property-declaration"
    ]
  }
}

Step 4

Run the command:

npm run lint

And check for errors in the terminal (some errors might not appear with Tslint extension)

Example:

Step 5

Fix the errors or edit your rules 🙂

Building real time charts with Angular 5, Google Charts, SignalR Core, .NET Core 2, Entity Framework Core 2 and SqlTable dependency, part 3

 

<< Back to part 2

We are done with the project setup and the back end implementation, now let’s focus the front end solution.

What does it look like ?

 

We have :

  • A folder that contains the gauge chart component (gaugeschart.component.html and gaugeschart.component.ts)
  • A folder that contains a gauge chart service and a Google Charts base service (google-gauges-chart.service.ts and google-charts.base.service.ts)
  • A folder that contains environments files
  • A folder that contains a strongly typed model for the gauge chart (gauge.ts)
  • Finally at the root of src folder the defaults files components and module (app component files and app module file)

 

Implementation

Gauge.ts

export class GaugeModel {
    public id: number;
    public cpu: number;
    public memory: number;
    public network: number;
}

google-charts.base.service.ts and google-gauges-chart.service.ts

For more explanations about these files work, you can check a preivous article I wrote some times before : http://anthonygiretti.com/2017/10/12/using-google-charts-in-angular-4-project-part-2/

declare var google: any;

export class GoogleChartsBaseService {
  constructor() { 
    google.charts.load('current', {'packages':['corechart','gauge']});
  }

  protected buildChart(data: any[], chartFunc: any, options: any) : void {
    var func = (chartFunc, options) => {
      var datatable = google.visualization.arrayToDataTable(data);
      chartFunc().draw(datatable, options);
    };   
    var callback = () => func(chartFunc, options);
    google.charts.setOnLoadCallback(callback);
  }
  
}

 

import { GoogleChartsBaseService } from './google-charts.base.service';
import { Injectable } from '@angular/core';

declare var google: any;

@Injectable()
export class GoogleGaugesChartService extends GoogleChartsBaseService {

  constructor() { super(); }

  public BuildGaugesChart(elementId: String, data: any[], config: any) : void {
    var chartFunc = () => { return new google.visualization.Gauge(document.getElementById(elementId)); };
    this.buildChart(data, chartFunc, config);
  }
}

Gauge chart component

As you can see, the component is decorelated from Google Charts because I inject within a Google Gauge chart service, it can be easily replaced by another Javascript library such as canvas.js

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

import { GoogleGaugesChartService } from '../services/google-gauges-chart.service';
import { OnChanges } from '@angular/core/src/metadata/lifecycle_hooks';

declare var google: any;


@Component({
  selector: 'gauges-chart',
  templateUrl: './gaugeschart.component.html'
})
export class GaugesChartComponent implements OnInit, OnChanges {

    @Input() data: any[];
    @Input() config: any;
    @Input() elementId: String;
    
    constructor(private _gaugesChartService: GoogleGaugesChartService) {}

    /* Only chart data !!!!!!! */
    ngOnChanges(changes) {
        if (changes.data != undefined) {
            this.data = changes.data.currentValue;
            this._gaugesChartService.BuildGaugesChart(this.elementId, this.data, this.config);
        }      
    }

    ngOnInit(): void {
        this._gaugesChartService.BuildGaugesChart(this.elementId, this.data, this.config); 
    }
}

I used ngOnInit for the component initialization with its first binding to data, then we need to “listen” any moditication of data that are broadcasted with the specific event ngOnChanges and we ensure it’s data that are modified if (changes.data != undefined)

Component Html :

<div id="{{elementId}}" style="width: 400px; height: 120px;"></div>

App component

This is most insteresting part, we will see hoz the client side library for SignalR works

import { AfterContentInit, AfterViewInit, Component, OnInit } from '@angular/core';

import { environment as Environment } from '../environments/environment';
import { GaugeModel } from '../models/gauge';
import { HubConnection } from '@aspnet/signalr-client';

declare var google: any;

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {

  private _hubConnection: HubConnection;

  public data:any[] = [
    ['Label', 'Value'],
    ['Memory', 0],
    ['CPU', 0],
    ['Network', 0]
  ];
  public elementId:String = "Gauge1";
  public config:any = {
    width: 400, height: 120,
    redFrom: 90, redTo: 100,
    yellowFrom:75, yellowTo: 90,
    minorTicks: 5
  };


  constructor() {
  }

  public ngOnInit() {
    
    this._hubConnection = new HubConnection(Environment.hubUrl);
    
    this._hubConnection
      .start()
      .then(() => this._hubConnection.invoke('GetGaugesData').catch(err => console.error(err)))
      .catch(err => console.log('Error while establishing connection :('));

    var that = this;
    this._hubConnection.on('GetGaugesData', (data: GaugeModel) => {
      this.data = [
        ['Label', 'Value'],
        ['Memory', data.memory],
        ['CPU', data.cpu],
        ['Network', data.network]
      ];
    });
    
  }

}

Firstly, we need to isntantiate a HubConnection, like this : this._hubConnection = new HubConnection(Environment.hubUrl); it takes in parameter the Hub Url defined in environment.ts :

export const environment = {
production:false,
hubUrl:"http://localhost:33383/gauges"
};

Then we open the connection and define a callback when it succeeds or handle error when it occur :

this._hubConnection
.start()
.then(() => this._hubConnection.invoke('GetGaugesData').catch(err => console.error(err)))
.catch(err => console.log('Error while establishing connection :('));

When it succeed we invoke the method that populate initially our chart :

this._hubConnection.invoke('GetGaugesData')

Once we are done with the inialization, we subscribe to any change and modify data :

this._hubConnection.on('GetGaugesData', (data: GaugeModel) => {
      this.data = [
        ['Label', 'Value'],
        ['Memory', data.memory],
        ['CPU', data.cpu],
        ['Network', data.network]
      ];
    });

Component Html :

<h2>Real-Time Gaugeschart by Google with SignalR Core and EntityFramework Core 2 / SQLTableDependency</h2>

<div class="divTable">
   <divclass="divTableBody">
      <divclass="divTableRow">
         <divclass="divTableCell"><gauges-chart [data]="data" [config]="config" [elementId]="elementId"></gauges-chart></div>
       </div>
   </div>
</div>

Component Css :

.divTable{
    display: table;
    width: 100%;
}

.divTableRow {
    display: table-row;
}

.divTableHeading {
    background-color: #EEE;
    display: table-header-group;
}

.divTableCell, .divTableHead {
    border: 1px solid #999999;
    display: table-cell;
    padding: 3px10px;
    text-align: center;
    margin: auto;
}

.divTableHeading {
    background-color: #EEE;
    display: table-header-group;
    font-weight: bold;
}

.divTableFoot {
    background-color: #EEE;
    display: table-footer-group;
    font-weight: bold;
}

.divTableBody {
    display: table-row-group;
}

Appmodule.ts :

import { AppComponent } from './app.component';
import { BrowserModule } from '@angular/platform-browser';
import { GaugesChartComponent } from './charts/gaugeschart.component';
import { GoogleChartsBaseService } from './services/google-charts.base.service';
import { GoogleGaugesChartService } from './services/google-gauges-chart.service';
import { NgModule } from '@angular/core';

@NgModule({
declarations: [
AppComponent,
GaugesChartComponent
],

imports: [
BrowserModule
],

providers: [GoogleChartsBaseService,GoogleGaugesChartService],
bootstrap: [AppComponent]
})

export class AppModule { }

Index.html

<!doctype html>
<html lang="en">
<head>
   <metacharset="utf-8">
   <title>Angular5SignalRCoreEFCore2GoogleCharts</title>
   <basehref="/">
   <script type="text/javascript"src="https://www.gstatic.com/charts/loader.js"></script>
   <metaname="viewport"content="width=device-width, initial-scale=1">
   <linkrel="icon"type="image/x-icon"href="favicon.ico">
</head>
<body>
    <app-root></app-root>
</body>
</html>

Dont forget to put Google Charts reference 🙂 (loader.js)

Tha ‘s the end did you enjoy it? 🙂

Try to play with data ! 😮

Using Google Charts in Angular 4 project, part 2

Integrate Google Charts and make reusable chart components

We previously saw how to use Google Charts in an classical HTML5 / Javascript page, now it’s time to see how we can make it work in an Angular 4 project. We will still use use the chart as sample (Google Pie Chart)

Let’s get started !

Step 1 :

Add the javascript library file in index.html :

<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>

Step 2 :

To make Google Charts more reusable as possible, let’s make a class named GoogleChartsBaseService that does the redundant job : loading the library and manage the callback that we described in the previous article:

declare var google: any;

export class GoogleChartsBaseService {
  constructor() { 
    google.charts.load('current', {'packages':['corechart']});
  }

  protected buildChart(data: any[], chartFunc: any, options: any) : void {
    var func = (chartFunc, options) => {
      var datatable = google.visualization.arrayToDataTable(data);
      chartFunc().draw(datatable, options);
    };   
    var callback = () => func(chartFunc, options);
    google.charts.setOnLoadCallback(callback);
  }
  
}

As you can I added this instruction at the top : declare var google: any;

This is because with TypeScript, and Typescript doesn’t know the definition of google variable, so we declared it, corresponding to the global variable declared in Google Charts library. Typescript will be able to transpile it.

buildChart encapsulate the callback definition, the data mapping (Array to Datatable), and the setOnLoadCallback method call.

Step 3 :

Let’s make a PieChartConfig model to bind it’s configuration :

export class PieChartConfig {
    title: string;
    pieHole: number

    constructor(title: string, pieHole: number) {
        this.title = title;
        this.pieHole = pieHole;
    }
}

title member is the chart title

pieHole is corresponding to the ratio of radii between the hole and the chart (value from 0 to 1)

Step 4 :

Let’s build GooglePieChartService , this service will extends GoogleChartsBaseService 

Because we use Typescript as you know, let’s add at the top : declare var google: any;

We need to encapsulate object google.visualization in a callback (arrow function works too) because this object is not set yet by the framework (during execution of setOnLoadCallback)

Here we are :

import { GoogleChartsBaseService } from './google-charts.base.service';
import { Injectable } from '@angular/core';
import { PieChartConfig } from './../Models/PieChartConfig';

declare var google: any;

@Injectable()
export class GooglePieChartService extends GoogleChartsBaseService {

  constructor() { super(); }

  public BuildPieChart(elementId: String, data: any[], config: PieChartConfig) : void {  
    var chartFunc = () => { return new google.visualization.PieChart(document.getElementById(elementId)); };
    var options = {
            title: config.title,
            pieHole: config.pieHole,
      };

    this.buildChart(data, chartFunc, options);
  }
}

Because it’s an injectable service (singleton), don’t forget to add it in providers array in app.module.ts

We built a base service and PieChartService, that means the logic is set in services and it provides more flexibilty in your app, we decoupled as much as possible Google Charts with our app

Step 5 :

It’s time to make reusable components

we need 3 parameters in input

1- Html id attribute of the div we want to fill with the chart

2- The chart configurations options

3- Data

Here we are:

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

import { GooglePieChartService } from './../../Services/google-pie-chart.service';
import { PieChartConfig } from './../../Models/PieChartConfig';

declare var google: any;


@Component({
  selector: 'pie-chart',
  templateUrl: './piechart.component.html'
})
export class PieChartComponent implements OnInit {

    @Input() data: any[];
    @Input() config: PieChartConfig;
    @Input() elementId: String;

    constructor(private _pieChartService: GooglePieChartService) {}

    ngOnInit(): void {
        this._pieChartService.BuildPieChart(this.elementId, this.data, this.config); 
    }
}

the pie chart conponent html file :

<div id="{{elementId}}" style="width: 800px; height: 400px;"></div>

We definitely have to declare them into app.module.ts as well

Step 6 :

Let’s add data, config and try !

We will build a component (named DashboardComponent) that display our reusable ours Pie charts and provide them data.

Ts file :

import { ComboChartConfig } from './../Models/ComboChartConfig';
import { Component } from '@angular/core';
import { PieChartConfig } from './../Models/PieChartConfig';

@Component({
  templateUrl: './dashboard.component.html',
  styleUrls: ['./dashboard.component.css']
})
export class DashboardComponent {
  title = 'Reusable charts sample';

  data1: any[];
  config1: PieChartConfig;
  elementId1: String;

  data2: any[];
  config2: PieChartConfig;
  elementId2: String;
  
  ngOnInit(): void {     

    //Piechart1 Data & Config
    this.data1 = [['Task', 'Hours per Day'],
    ['Eat',      3],
    ['Commute',  2],
    ['Watch TV', 5],
    ['Video games', 4],
    ['Sleep',    10]];

    this.config1 = new PieChartConfig('My Daily Activities at 20 years old', 0.4);
    this.elementId1 = 'myPieChart1';

    //Piechart2 Data & Config
    this.data2 = [['Task', 'Hours per Day'],
                  ['Work',     11],
                  ['Eat',      2],
                  ['Commute',  2],
                  ['Watch TV', 2],
                  ['Sleep',    7]]

    this.config2 = new PieChartConfig('My Daily Activities at 30 years old', 0.4);
    this.elementId2 = 'myPieChart2';
  }

}

HTML file :

<h2>{{title}}</h2>

<div class="divTable">
    <div class="divTableBody">
        <div class="divTableRow">
            <div class="divTableCell"><pie-chart [data]="data1" [config]="config1" [elementId]="elementId1"></pie-chart></div>
            <div class="divTableCell"><pie-chart [data]="data2" [config]="config2" [elementId]="elementId2"></pie-chart></div>
        </div>
    </div>
</div>

And here is the associated css in dashboard.component.css

Don’t forget to declare that component into app.module.ts

And now …..

Awesome right? 🙂

Now you know how to make reusable Google Charts Angular 4 components! 🙂 🙂 🙂

Here the Url of the project if you want to test in your browser : http://demoangular4.azurewebsites.net/

If you want to get the complete source code you can find it there, you can also contribute! : https://github.com/AnthonyGiretti/Angular4-GoogleCharts