Creating a chart with D3 and TypeScript – part 1

Introduction

This series of articles is basically about creating a JavaScript chart from scratch, using D3 and TypeScript. I will take it that you don’t know much about D3 and TypeScript, but know what those two are and do. I will also assume that you already used JavaScript previously, and that your box is ready for web development (see what I mean in my previous articles). As a teaser, here is the result we will try to achieve:

chart

Through this series of articles, I will try to get you:

  • understanding how D3 works and how easy it can be to create simple things
  • seeing an added value in using TypeScript
  • creating a basic build process that will help your development
  • structuring a JavaScript framework

You can find the completed code for this article here.

Updated on 2016-04-12 to use the new D3 types (generics).

Folders structure

The first thing to do is to create a basic folders structure to store our files. There is no rule in the way to do it, I will just follow a structure that is commonly seen in projects on GitHub. Let’s start by creating a root folder, I called mine charting (how original). In this folder, I will create three sub-folders:

  • src: the folder that will contain all the sources of my chart
  • dist: the folder that will contain the ready-for-distribution files
  • test: the folder that will contain the test of the chart

I will not be writing unit tests in this example (I know, I should), but we will want to see how our chart looks like, so we will need a page displaying our chart.

The tools

Bower

As I mentioned, we will be using D3 and TypeScript, and create a basic build process. We will therefore use bower and npm for this chart. To get started, open a command shell, go to your root directory and type

bower init

You leave all the answers as they are, the goal is just to have the file initialized. As D3 will be a requirement, we will install it using

bower install d3 --save

This should add a new line in you bower.json file, and add a d3 folder under bower_components at your root folder.

Build process

I think it is a good idea to set up a build process as soon as possible in the development of a library, even if very small. Especially as we will be working on  something graphical that we will want to see changing, and using supersets, namely TypeScript and less, that must be compiled to JavaScript and css respectively.

I will use gulp to create my build process, grunt would be as good a choice. Gulp is an npm package, we will therefore need to initialize npm via

npm init

Here again, default responses will do. This will create a file called package.json in your root directory. We will then need to install gulp as well as some plugins that will be useful (TypeScript and less compilation) by typing

npm install --save-dev gulp gulp-typescript gulp-less typescript

While updating the article, I realized that typings is missing to be able to install types definitions. I will come back soon to fix this.

Finally, we will install BrowserSync, that will allow auto-reload of the page when our files get changed

npm install --save-dev browser-sync

I skipped ts linting for simplicity, but if you plan to extend on this, I highly recommend you include it to ensure high code quality.

The next step is to configure our build process. The main steps that will be included are:

  • TypeScript compilation to dist folder
  • less compilation to dist folder
  • watch ts files for changes and recompile on change
  • watch less files for changes and recompile on change
  • watch all files in dist folder for change and reload browser on change

I will not go into the details of configuring the build process. Create a file called gulpfile.js in your root directory and use the code below

var gulp = require('gulp');
var typescript = require('gulp-typescript');
var browserSync = require('browser-sync');
var less = require('gulp-less');

gulp.task('ts', function() {
    return gulp.src(['./src/**/*.ts', 'typings/browser.d.ts'])
        .pipe(typescript({
            out: 'charting.js'
        }))
        .pipe(gulp.dest('./dist'));
});

gulp.task('less', function() {
    return gulp.src('./src/**/*.less')
        .pipe(less({

        }))
        .pipe(gulp.dest('./dist'));
});

gulp.task('watch-less', function() {
    gulp.watch(['./src/**/*.less'], ['less']);
});

gulp.task('watch-ts', function() {
    gulp.watch(['./src/**/*.ts'], ['ts']);
});

gulp.task('browserSync', function() {
    browserSync.init({
        server: './',
        index: './test/index.html',
        port: 3030,
        files: ['./dist/*.*', './test/index.html']
    });
});

gulp.task('watch', ['watch-ts', 'watch-less', 'browserSync'], function() {

});

If you notice, the browserSync task requires an index.html file in the test folder. Let’s create it and initiate it with basic html5 skeleton

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Test charting</title>
    <link rel="stylesheet" href="../dist/charting.css">
</head>

<body>
    <h1>Hello world</h1>
</body>
</html>

If everything worked fine, go to you shell and type

gulp watch

This should open your browser, and you should see your hello world page.

The chart

Ok, now that we’re all set up, let’s get to work. Before we can draw anything, we will need a container for our chart, as well as references to our libraries. Let’s jump to our index.html and modify it like this

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Test charting</title>
    <link rel="stylesheet" href="../dist/charting.css">
</head>

<body>
    <svg style="width:800px;height:600px;" id="test">

    </svg>
    <div>
        Update
    </div>
    <a href="http://../bower_components/d3/d3.min.js">http://../bower_components/d3/d3.min.js</a>
    <a href="http://../dist/charting.js">http://../dist/charting.js</a>
</body>
</html>

 Axes

Let’s start by drawing an axis, say the horizontal axis. An axis requires 2 main things to be built:

  • a scale, containing information about where things should be displayed
  • an container in which it will be drawn

Container

The axis has to be drawn somewhere. we will thus have to tell D3 where to draw that axis. From our html, we already have an svg element with the id #test. To keep things organized, we will draw our axis in a separate group inside the svg. Thus, to get started, create a file called charting.ts (pay attention to the extension, this is a TypeScript file), and add the following code:

/// <reference path="../typings/d3/d3.d.ts"/>;
module charting {
    export class chart {
        private _group: d3.Selection<any>;

        constructor(container: any) {
            this.init(container);
        }

        private init(container) {
            this._group = d3.select(container).append('g');
        }

    }
}

What this does is:

  • define a TypeScript module. A module can be seen as a way of namespacing you code, and avoid polluting the global scope with numerous variables. Everything we will do will be in the charting module.
  • define a chart class, that we will use to build our chart. The constructor for this class takes a container as argument, we will send the svg there
  • define an init method where we append our group
  • keep a private variable containing the created group

You can see how simple it is to add things with D3. You simply select your container using the select method, and then append the element you want, in our case a group ().

Let’s go back to the index.html and create an instance of chart in our svg. Inside the empty script at the bottom, add following code:

var axis = new charting.chart('#test');

Save all the files, and if your watchers were active, your browser should reload. By right-clicking on the svg and selecting Inspect element, you should see that the svg now contains a group. Yay!

Scale

Now that we have a container, let’s add a scale to draw the axis. A scale has two important properties to set:

  • a range: an array of values, representing the minimum and maximum pixels (i.e. x or y) of the scale
  • a domain: an array of values containing the values represented by the scale

To understand it better, let’s create a new method called draw in our chart class, like this:

draw() {
var scale = d3.scale.linear();

scale.domain([0, 1]);
scale.range([0, 800]);
var axis = d3.svg.axis();
axis.scale(scale);
this._group.call(axis);
}

This method will create a scale accepting values from 0 to 1 and display them between 0 and 800 pixels. The scale is then associated to the axis, and the axis is rendered in the group.

In our index.html we will call our draw method:

axis.draw()

Save all your files, and you should get something like this:

axis

 Conclusions

This first article set the ground for the development of a chart using TypeScript and D3. It showed how to

  • architect a basic framework
  • create a build process
  • create a test page
  • create a TypeScript module
  • create a TypeScript class
  • draw an axis using D3

In the next article, we will draw a vertical axis, change the type of the x-axis to make it a time scale, style the axes and display data on the chart.
Code for the completed example can be found (here)[https://github.com/Ledragon/charting/tree/c8bdf1ea838554451db3cfe56e64f6b62d92a8be].

Advertisements
Leave a comment

12 Comments

  1. Timely blog post . I was enlightened by the points . Does someone know where I might be able to get access to a fillable a form version to fill out ?

    Like

    Reply
  2. I tried your code, but it doesn’t work. It only shows text. I didn’t dig deeper into this, but I found out that the line shows if I use the longer init() method from the second part of the series. Maybe you could provide the complete working example at the end of the post. Thanks!

    Like

    Reply
  3. Hello, thank you for the detailed tutorial. Do you know why I started having errors like
    “`
    xAxis.ts(5,19): error TS2503: Cannot find namespace ‘D3’.

    “`
    I have TS 1.5, NodeJS 4.4 and the needed typings installed

    Like

    Reply
    • Hello, glad you liked it.
      Dependin g on the version of the types you have, D3 namespace might be called d3 (lower case), as it was changed after I published the article. Also, make sure you are using version 3 of d3 to follow the tutorial. Version 4 was released in June, and some of the APIs changed, among other regarding axes.

      Like

      Reply
  1. Creating a chart with D3 and TypeScript – part 2 | Wandering in the community
  2. Responding to D3 events in TypeScript | Wandering in the community
  3. Why does d3 not work properly with TypeScript? - Das Web Developer
  4. D3 v4 and TypeScript: getting set up | Wandering in the community

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: