Drawing histograms using D3 and typescript

Drawing histograms using D3 and typescript

If you ever played with D3, you’ve most likely found https://bl.ocks.org - Mike Bostock’s site where he explains how to draw D3 charts from very basic to quite complex ones. On several occasions I followed his examples to draw charts on my sites.

Recently, I started working on a TypeScript app and had to port JS examples to TypeScript.

I used one of Mike’s samples here’s the histogram from his site to explain how to translate it to TypeScript and wrap in a form of an Angular2 component.

Click here to find a GIST with the full source code

Prerequisites

In order to add this example to your TS app you’ll need following packages:

npm install d3-axis d3-random d3-scale d3-selection --save

Differences between JS and TS code

Imports

This example follows the modular approach for D3 v4. Finding the correct module to import is a matter of going to D3 v4 API reference and finding which d3-x module contains types you need.

import { select } from 'd3-selection';
import { scaleLinear } from 'd3-scale';
import { range, histogram, max } from 'd3-array';
import { format } from 'd3-format';
import { randomBates } from 'd3-random';
import { axisBottom } from 'd3-axis';

Using TS generics properly

When using TypeScript we need to specify types we use in generic classes.

And JavaScript like this

var x = d3.scaleLinear().rangeRound([0, width]);

becomes

let x = scaleLinear<number>().rangeRound([0, width]);

Avoding minor @types issues

For the d3-array version 1.2.0 passing x.domain() as histogram.domain() argument, raises a type error.

After drilling into d3-array.ts.d definition I’ve found this:

interface Histogram<T> {
    /* REMOVED FOR BREVITY */

    /**
    * 2: numeric data → [min, max]
    * @link https://github.com/d3/d3-array#histogram_domain
    */
    // SOMEDAY => any → => $$.Orderable
    domain():(values:$$.Orderable[]) => any;
    domain(value:[$$.Orderable, $$.Orderable]):this;
    domain(value:(values:$$.Orderable[]) => any[]):this;

    /* REMOVED FOR BREVITY */
}

So, I decided to go with the third option and…

var bins = d3.histogram()
              .domain(x.domain())
              .thresholds(x.ticks(20))

became

let generator = histogram<number>()
                .domain(d => x.domain())
                .thresholds(x.ticks(20));

Extra - using Angular2 component template with D3

In order to use D3 to modify part of your Angular template DOM, you’ll need to use ElementRef to pass the template into your component code.

import { Component, OnInit, ElementRef } from '@angular/core';
import { select } from 'd3-selection';

@Component({
    selector:'histogram',
    template:'<h1>Hello histogram</h1>\
              <svg id="hist" width="960" height="500"></svg>'
})
export class HistogramComponent implements OnInit {

    el: HTMLElement;

    constructor(private elementRef: ElementRef){
        this.el = elementRef.nativeElement;
    }

    ngOnInit(): void {
        this.drawHistogram();
    }

    drawHistogram(){
        let hist = select(this.el).select('#hist');
        // Follow with your regular D3 flow
    }

Click here to find a GIST with the full source code

- to blog -

blog built using modified cayman-theme by Jason Long. LICENSE