Skip to main content

Data visualization in Angular using D3.js

 As more organizations focus on collecting and analyzing data, one of the challenges that product teams face is presenting that data in a useful way. While many software developers default to list and table views, these presentation formats have a tendency to overwhelm users.

Data visualization helps you better communicate meaning in your data by portraying it in a graphical format. This often means bar charts, scatter plots, or pie charts. Because most data visualizations on the web are generated on the frontend, JavaScript is the language of choice for data visualization libraries.

Of the available libraries, D3.js is one of the most popular. It allows you to create data visualizations by manipulating the DOM based on dynamic data. D3 is reactive, meaning that instead of generating a static image on the server and serving it to the client, it uses JavaScript to “draw” HTML elements onto your webpage. This makes D3 powerful, but also a little harder to use than some other charting libraries.

Angular is maintained by Google and is one of the most popular open-source frontend web frameworks. In this tutorial, you’ll see how you can add data visualizations to your Angular app using D3. You’ll go through the process of setting up Angular and D3, adding three common types of charts, and importing data from a third-party API or local CSV file. By the end, you should have a starting point for creating your own data visualizations using D3 and Angular.

Setting up Angular and D3

Angular uses a command line interface to help you generate new applications and components. You just need to have Node.js and npm installed to proceed. If you’re new to using Angular, you can read the local setup guide to learn more about the process, or if you’d like to skip the walkthrough and download the final application, you can get the code from GitHub. If you’d like to go through the entire process, read on!

First, install the Angular CLI. The command line tool offers a quick way to start new Angular projects:

npm install -g @angular/cli

Next, create a new Angular app. You can call it whatever you want, but I’ll use the name angular-d3:

ng new angular-d3

Finally, navigate into the new project:

cd angular-d3/

Next, install D3 and the D3 type definitions from npm. Type definitions will allow TypeScript to apply type hints to the external D3 code.

npm install d3 && npm install @types/d3 --save-dev

Next, create three new components using the Angular CLI. In the following steps, you’ll use D3 to generate data visualizations within each one.

First, the bar component:

ng g component bar

Next, the pie component:

ng g component pie

And the scatter component:

ng g component scatter

These components are now available in the src/app/ directory and Angular added them to your app.module.ts file, but you still need to insert the components using their selectors. Open up your src/app/app.component.html file and replace its contents with the following:

<header>
  <h1>Angular + D3</h1>
</header>
<app-bar></app-bar>
<app-pie></app-pie>
<app-scatter></app-scatter>

Finally, you can make the site look a little prettier by adding new.css to your <head>. Open up the src/index.html file and add the following lines between the <head></head> tags:

<link rel="stylesheet" href="https://fonts.xz.style/serve/inter.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@exampledev/new.css@1.1.2/new.min.css">

You’re ready to test it out. From your terminal, run ng serve --open. Your browser should open up http://localhost:420``0, and you’ll see something like this:

New Angular App Using D3.js
A new Angular application using D3.js.

Now that your Angular app is ready, let’s add three charts it: a bar chart, pie chart, and scatter plot.

Creating a bar chart

The first data visualization you’ll add is a bar chart. Bar charts are typically used to show relative values of different categories of data. The data we’ll use is the number of stars each popular frontend web development framework has:

[
    {"Framework": "Vue", "Stars": "166443", "Released": "2014"},
    {"Framework": "React", "Stars": "150793", "Released": "2013"},
    {"Framework": "Angular", "Stars": "62342", "Released": "2016"},
    {"Framework": "Backbone", "Stars": "27647", "Released": "2010"},
    {"Framework": "Ember", "Stars": "21471", "Released": "2011"}
]

Angular components usually consist of four files: an HTML template file, a CSS or SCSS stylesheet, a spec (test) file, and a TypeScript component file. Open up the template file (bar.component.html) and add the following header and figure:

<h2>Bar Chart</h2>
<figure id="bar"></figure>

You’ll use D3 to add the chart inside the figure using a CSS selector. Next, open up the TypeScript component file (bar.component.ts) and add the following properties:

...
export class BarComponent implements OnInit {
  private data = [
    {"Framework": "Vue", "Stars": "166443", "Released": "2014"},
    {"Framework": "React", "Stars": "150793", "Released": "2013"},
    {"Framework": "Angular", "Stars": "62342", "Released": "2016"},
    {"Framework": "Backbone", "Stars": "27647", "Released": "2010"},
    {"Framework": "Ember", "Stars": "21471", "Released": "2011"},
  ];
  private svg;
  private margin = 50;
  private width = 750 - (this.margin * 2);
  private height = 400 - (this.margin * 2);
...

The first private property, data, hardcodes the data needed to generate the chart. You’ll see later how to use data from a file or API, but this will let you get started.

The svg property will be used in the class to store the SVG image that D3 draws onto the DOM. The other properties set a height, width, and margin for the chart. While it’s possible to create responsive charts with D3, I won’t explore them in this tutorial.

Next, create a method in the BarComponent called createSvg(). This selects the element in the DOM and inserts a new SVG with a <g> element:

private createSvg(): void {
    this.svg = d3.select("figure#bar")
    .append("svg")
    .attr("width", this.width + (this.margin * 2))
    .attr("height", this.height + (this.margin * 2))
    .append("g")
    .attr("transform", "translate(" + this.margin + "," + this.margin + ")");
}

Now create a method called drawBars() that will add the bars using the svg property:

private drawBars(data: any[]): void {
    // Create the X-axis band scale
    const x = d3.scaleBand()
    .range([0, this.width])
    .domain(data.map(d => d.Framework))
    .padding(0.2);

    // Draw the X-axis on the DOM
    this.svg.append("g")
    .attr("transform", "translate(0," + this.height + ")")
    .call(d3.axisBottom(x))
    .selectAll("text")
    .attr("transform", "translate(-10,0)rotate(-45)")
    .style("text-anchor", "end");

    // Create the Y-axis band scale
    const y = d3.scaleLinear()
    .domain([0, 200000])
    .range([this.height, 0]);

    // Draw the Y-axis on the DOM
    this.svg.append("g")
    .call(d3.axisLeft(y));

    // Create and fill the bars
    this.svg.selectAll("bars")
    .data(data)
    .enter()
    .append("rect")
    .attr("x", d => x(d.Framework))
    .attr("y", d => y(d.Stars))
    .attr("width", x.bandwidth())
    .attr("height", (d) => this.height - y(d.Stars))
    .attr("fill", "#d04a35");
}

Finally, call both of these methods in your BarComponent‘s ngOnInit() method:

ngOnInit(): void {
    this.createSvg();
    this.drawBars(this.data);
}

If you stopped the Angular server from the previous step, restart it (ng serve) and visit localhost:4200 in your browser. You should see your new bar chart:

A D3 Bar Chart In Angular
A D3 bar chart in Angular.

Creating a pie chart

A pie chart is a good way to show the relative values of different data. In this case, you’ll use it to visualize the market share of different frontend frameworks based on GitHub stars.

The first step is to update the component’s HTML file (pie.component.html) with a new figure and title:

<h2>Pie Chart</h2>
<figure id="pie"></figure>

Because this chart uses the same set of data as the bar chart, the component’s class starts off looking similar. Update the pie.component.ts file with your data and the following private properties:

...
export class PieComponent implements OnInit {
  private data = [
    {"Framework": "Vue", "Stars": "166443", "Released": "2014"},
    {"Framework": "React", "Stars": "150793", "Released": "2013"},
    {"Framework": "Angular", "Stars": "62342", "Released": "2016"},
    {"Framework": "Backbone", "Stars": "27647", "Released": "2010"},
    {"Framework": "Ember", "Stars": "21471", "Released": "2011"},
  ];
  private svg;
  private margin = 50;
  private width = 750;
  private height = 600;
  // The radius of the pie chart is half the smallest side
  private radius = Math.min(this.width, this.height) / 2 - this.margin;
  private colors;
...

The big difference here is the addition of the radius and colors properties. Because pie charts use a circle instead of a rectangle to display data, that rad``i``us property ensures that the chart fits within the defined figure’s bounds. You’ll use colors to define the colors for the pie chart in a coming step.

Next, create a private method called createSvg(). This will select the element on the DOM and add the <g> element where D3 will draw your pie chart:

private createSvg(): void {
    this.svg = d3.select("figure#pie")
    .append("svg")
    .attr("width", this.width)
    .attr("height", this.height)
    .append("g")
    .attr(
      "transform",
      "translate(" + this.width / 2 + "," + this.height / 2 + ")"
    );
}

In this pie chart, you’ll use an ordinal scale to create a discrete color for each section of the chart. You could define each to be the dominant color of the framework, but I think a monochromatic scheme looks nicer.

private createColors(): void {
    this.colors = d3.scaleOrdinal()
    .domain(this.data.map(d => d.Stars.toString()))
    .range(["#c7d3ec", "#a5b8db", "#879cc4", "#677795", "#5a6782"]);
}

Create a method to draw the chart and add labels. This method uses <path> elements to create arcs for each framework and fill them with the colors defined in the createColors method above.

private drawChart(): void {
    // Compute the position of each group on the pie:
    const pie = d3.pie<any>().value((d: any) => Number(d.Stars));

    // Build the pie chart
    this.svg
    .selectAll('pieces')
    .data(pie(this.data))
    .enter()
    .append('path')
    .attr('d', d3.arc()
      .innerRadius(0)
      .outerRadius(this.radius)
    )
    .attr('fill', (d, i) => (this.colors(i)))
    .attr("stroke", "#121926")
    .style("stroke-width", "1px");

    // Add labels
    const labelLocation = d3.arc()
    .innerRadius(100)
    .outerRadius(this.radius);

    this.svg
    .selectAll('pieces')
    .data(pie(this.data))
    .enter()
    .append('text')
    .text(d => d.data.Framework)
    .attr("transform", d => "translate(" + labelLocation.centroid(d) + ")")
    .style("text-anchor", "middle")
    .style("font-size", 15);
}

D3’s centroid function allows you to put labels in the calculated centroid of each slice of the pie. In this case, by setting the innerRadius(100), the labels will be slightly outside the true centroid. You can adjust these numbers to reposition them wherever you think they look best.

Finally, call all three of these methods in the ngOnInit() method:

ngOnInit(): void {
    this.createSvg();
    this.createColors();
    this.drawChart();
}

Head back over to your browser to see the new pie chart in your Angular application.

A D3 Pie Chart In Angular
A D3 pie chart in Angular.

Creating a scatter plot

The last type of data visualization you’ll create for this tutorial is a scatter plot. Scatter plots give us the ability to show the relationship between two pieces of data for each point in the graph. In this case, you’ll look at the relationship between the year that each framework was released and the number of stars it currently has.

Start off by updating the HTML template file (scatter.component.html) in the same way as you did above:

<h2>Scatter Plot</h2>
<figure id="scatter"></figure>

Because this scatter plot uses the same data and figure size, it starts off with the same properties as the bar chart did:

...
export class ScatterComponent implements OnInit {
  private data = [
    {"Framework": "Vue", "Stars": "166443", "Released": "2014"},
    {"Framework": "React", "Stars": "150793", "Released": "2013"},
    {"Framework": "Angular", "Stars": "62342", "Released": "2016"},
    {"Framework": "Backbone", "Stars": "27647", "Released": "2010"},
    {"Framework": "Ember", "Stars": "21471", "Released": "2011"},
  ];
  private svg;
  private margin = 50;
  private width = 750 - (this.margin * 2);
  private height = 400 - (this.margin * 2);
...

In fact, the createSvg method is the same as the bar chart, too:

private createSvg(): void {
    this.svg = d3.select("figure#scatter")
    .append("svg")
    .attr("width", this.width + (this.margin * 2))
    .attr("height", this.height + (this.margin * 2))
    .append("g")
    .attr("transform", "translate(" + this.margin + "," + this.margin + ")");
}

If your application has many bar and scatter plots that use similar properties, you might want to try using inheritance to cut down on the amount of duplicate code.

Next, create a new drawPlot() method to create the x- and y-axes of your plot and add the dots to the canvas. This method makes the points semi-transparent and adds the name of each framework as a label.

private drawPlot(): void {
    // Add X axis
    const x = d3.scaleLinear()
    .domain([2009, 2017])
    .range([ 0, this.width ]);
    this.svg.append("g")
    .attr("transform", "translate(0," + this.height + ")")
    .call(d3.axisBottom(x).tickFormat(d3.format("d")));

    // Add Y axis
    const y = d3.scaleLinear()
    .domain([0, 200000])
    .range([ this.height, 0]);
    this.svg.append("g")
    .call(d3.axisLeft(y));

    // Add dots
    const dots = this.svg.append('g');
    dots.selectAll("dot")
    .data(this.data)
    .enter()
    .append("circle")
    .attr("cx", d => x(d.Released))
    .attr("cy", d => y(d.Stars))
    .attr("r", 7)
    .style("opacity", .5)
    .style("fill", "#69b3a2");

    // Add labels
    dots.selectAll("text")
    .data(this.data)
    .enter()
    .append("text")
    .text(d => d.Framework)
    .attr("x", d => x(d.Released))
    .attr("y", d => y(d.Stars))
}

Another important difference in the scatter plot is that your x-axis uses dates instead of strings (like you did in the bar chart), so you have to call .tickFormat(d3.format("d")) to format them properly.

Finally, call both methods from your ngOnInit() method:

ngOnInit(): void {
    this.createSvg();
    this.drawPlot();
}

Head back over to your browser to see the final plot rendered in your Angular application.

D3 Scatter Plot In Angular
A D3 scatter plot in Angular.

Loading data from external sources

So far, you’ve hardcoded data into your Angular components, but that’s probably not realistic. There are several ways you can load data from external sources, so let’s look at two of the most common patterns you might use in D3.

Loading a CSV file

Most spreadsheets can be exported as CSV files. CSV is a text-based data storage format that uses commas and line breaks to separate values in the file. Because CSV is such a common data format, D3 provides native support for loading CSV files that are publicly available.

To demonstrate using a CSV file, create a new file in your Angular application’s src/assets/ directory called frameworks.csv. Add the following text to the file:

Framework,Stars,Released
Vue,166443,2014
React,150793,2013
Angular,62342,2016
Backbone,27647,2010
Ember,21471,2011

Next, open up the bar chart component (bar.component.ts) and update the ngOnInit() method to call D3’s csv() method:

ngOnInit(): void {
    this.createSvg();
    // Parse data from a CSV
    d3.csv("/assets/frameworks.csv").then(data => this.drawBars(data));
}

D3 can load CSVs from your Angular application or a third-party URL and makes the data available as an array of objects in the resulting promise. If you go back to the browser, you should see the same bar chart as before, but this time it’s using data from the frameworks.csv file instead of the Angular component.

Getting data from a JSON API

Another common data format used in web APIs is JSON. JSON is a string representation of JavaScript objects and is commonly returned by REST and GraphQL APIs. D3 natively supports JSON data, so it makes it really easy to integrate with your Angular application.

First, you’ll need a JSON API endpoint or file. I uploaded the framework data used throughout this tutorial to JSONbin, a free JSON file hosting platform. You can access this data here.

To use this endpoint, open up the bar.component.ts file again and update the ngOnInit() method with the following:

ngOnInit(): void {
    this.createSvg();
    // Fetch JSON from an external endpoint
    d3.json('https://api.jsonbin.io/b/5eee6a5397cb753b4d149343').then(data => this.drawBars(data));
}

As in the CSV example, D3’s json method returns a promise with your data parsed as an array of objects. By passing this in, your bar chart can now use the JSON API endpoint as its data source.

Conclusion

When you need to create rich data visualizations in your Angular app, D3 is a great choice. While it may take some time to master, D3 is very powerful and can be used to create almost any data visualization you can imagine. It also makes accessing datasets from CSVs or JSON APIs really easy.

If you’d like to learn more ways to customize your bar charts, I’d recommend looking at the official documentation or the D3 Graph Gallery for more examples.

Comments

Popular posts from this blog

How to use Ngx-Charts in Angular ?

Charts helps us to visualize large amount of data in an easy to understand and interactive way. This helps businesses to grow more by taking important decisions from the data. For example, e-commerce can have charts or reports for product sales, with various categories like product type, year, etc. In angular, we have various charting libraries to create charts.  Ngx-charts  is one of them. Check out the list of  best angular chart libraries .  In this article, we will see data visualization with ngx-charts and how to use ngx-charts in angular application ? We will see, How to install ngx-charts in angular ? Create a vertical bar chart Create a pie chart, advanced pie chart and pie chart grid Introduction ngx-charts  is an open-source and declarative charting framework for angular2+. It is maintained by  Swimlane . It is using Angular to render and animate the SVG elements with all of its binding and speed goodness and uses d3 for the excellent math functio...

JavaScript new features in ES2019(ES10)

The 2019 edition of the ECMAScript specification has many new features. Among them, I will summarize the ones that seem most useful to me. First, you can run these examples in  node.js ≥12 . To Install Node.js 12 on Ubuntu-Debian-Mint you can do the following: $sudo apt update $sudo apt -y upgrade $sudo apt update $sudo apt -y install curl dirmngr apt-transport-https lsb-release ca-certificates $curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash - $sudo apt -y install nodejs Or, in  Chrome Version ≥72,  you can try those features in the developer console(Alt +j). Array.prototype.flat && Array.prototype. flatMap The  flat()  method creates a new array with all sub-array elements concatenated into it recursively up to the specified depth. let array1 = ['a','b', [1, 2, 3]]; let array2 = array1.flat(); //['a', 'b', 1, 2, 3] We should also note that the method excludes gaps or empty elements in the array: let array1 ...

Understand Angular’s forRoot and forChild

  forRoot   /   forChild   is a pattern for singleton services that most of us know from routing. Routing is actually the main use case for it and as it is not commonly used outside of it, I wouldn’t be surprised if most Angular developers haven’t given it a second thought. However, as the official Angular documentation puts it: “Understanding how  forRoot()  works to make sure a service is a singleton will inform your development at a deeper level.” So let’s go. Providers & Injectors Angular comes with a dependency injection (DI) mechanism. When a component depends on a service, you don’t manually create an instance of the service. You  inject  the service and the dependency injection system takes care of providing an instance. import { Component, OnInit } from '@angular/core'; import { TestService } from 'src/app/services/test.service'; @Component({ selector: 'app-test', templateUrl: './test.component.html', styleUrls: ['./test.compon...