In this tutorial we are going to see how to use d3 in angular. D3 is helps to build real real world reporting with different forms of visualizations using bar charts and pie charts or any kind that you can draw with SVG and canvas.
Today we are going to useĀ d3 with angular so that we can combine both features of both angular and d3 for our project. In this angular d3 example we are going to create a angular project from scratch and import d3 in angular and create a simple chart inside it.
It will help us to know how can we integrate and customize d3 chart inside angular. Let’s get started.
What are we going to do ?
- Install fresh project of angular.
- Install d3 dependencies.
- Prepare JSON Data
- Create a chart component.
- Draw bar chart in chart component.
How to Draw Bar Chart With Angular & D3
To draw bar chart with angular first we have to make our project with d3 dependency ready for angular and start drawing the chart. Let’s get on with clear angular d3 example so, that it would be easy to know how it works.
Step 1 – Installing Fresh Project of Angular
Since you are already searching for d3 I assume that you already know how to setup an angular project and get started. If you don’t know comment down below.
Step 2 – Install D3 Dependencies
To make angular d3 to ready to build charts with d3 we need to install some dependency so go to your project root and install the dependency below
npm install d3 && @types/d3
Step 3 – Preparing JSON Data
Once the dependencies are install it is time to prepare JSON data for the charts. So it highly depends on how you provide the data but this is how I prepared it. I find it most efficient way to control it over the chart.
Also, prepare the domain by calculating the highest value from your chart data. See the copy of my app.component.ts
file below to go in detail.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
import { Component } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { data: {name: string, series: { name: string, value: number }[] }[]; barColor = ['#a9ce97', '#a5b5de']; domain = [100, 1000]; constructor() { this.data = [ { name: 'Row1', series: [ {name: 'Bar1', value: 150}, {name: 'Bar2', value: 200} ], }, { name: 'Row2', series: [ {name: 'Bar1', value: 300}, {name: 'Bar2', value: 400} ], }, { name: 'Row3', series: [ {name: 'Bar1', value: 500}, {name: 'Bar2', value: 1000} ], } ]; } } |
Step 4 – Creating a Chart Component
Now let us create a chart component which you can use it across all your projects and reuse it in entire application rather than writing everything inside app.component.ts
file. I am going to create bar chart so I will create a component named bar chart.
Go to terminal and type ng g c BarChartVertical --skipTests=true
Once the component is created it would have been automatically registered in app.module.ts
. If there is not present in app.module.ts
in declarations section, add it.
Once everything done add Bar Chart Vertical component to your app.component.html
1 |
<app-bar-chart-vertical [data]="data" [domain]="domain" [barColors]="barColor"></app-bar-chart-vertical> |
Step 5 – Drawing an Bar Chart Component
Finally, it is time to draw the bar chart component. You have recently checkout this blog, I have recently drawn vertical bar chart using d3 in core JavaScript. If you haven’t been through it, have time go through it.
I am going to draw same chart but inside angular which is going to use the power of typescript. Now create the template reference where you want to draw your chart by copying and paste this in your bar-chart-vertical.component.html
1 |
<div #svgContainer></div> |
Copy these code below inside bar-chart-vertical.component.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
import { AfterViewInit, Component, ElementRef, HostListener, Input, OnChanges, SimpleChanges, ViewChild } from '@angular/core'; import * as d3 from 'd3'; import {ScaleBand} from 'd3'; @Component({ selector: 'app-bar-chart-vertical', templateUrl: './bar-chart-vertical.component.html', styleUrls: ['./bar-chart-vertical.component.css'] }) export class BarChartVerticalComponent implements AfterViewInit, OnChanges { @Input() data!: { name: string, series: { name: string, value: number }[] }[]; @Input() height = 300; @Input() margin = {top: 10, left: 50, right: 10, bottom: 20}; @Input() innerPadding = 0.1; @Input() outerPadding = 0.1; @Input() seriesInnerPadding = 0.1; @Input() domain = [0, 1000]; @Input() barColors = ['#00aeef', '#f98e2b', '#7C77AD']; public svg!: d3.Selection<SVGGElement, unknown, null, undefined>; public isRendered = false; @ViewChild('svgContainer', {read: ElementRef, static: true}) svgContainerRef!: ElementRef<HTMLDivElement>; constructor() { } @HostListener('window:resize') onResize() { this.createChart(); } ngOnChanges(changes: SimpleChanges) { if (this.isRendered) { this.createChart(); } } ngAfterViewInit(): void { this.createChart(); this.isRendered = true; } private createSVG(): void { this.svg = d3.select(this.svgContainerRef.nativeElement) .append('svg') .attr('width', '100%') .attr('height', this.height) .append('g') .attr('width', '100%') .attr('transform', 'translate(0, 0)') .attr('class', 'bar-chart-vertical'); } private isDataValid(): boolean { return this.data && this.data.length > 0; } private getBandScale(domain: string[], range: any, innerPadding = 0, outerPadding = 0) { const scale: any | ScaleBand<string> = d3.scaleBand() .range(range) .domain(domain) .paddingInner(innerPadding) .paddingOuter(outerPadding); scale.type = 'BAND'; return scale; } private createChart(): void { if (!this.isRendered) { this.createSVG(); } if (this.isDataValid()) { const margin = { top: this.margin.top, right: this.margin.right, bottom: this.margin.bottom, left: this.margin.left, } let height = this.height - margin.top - margin.bottom; const width = this.svgContainerRef.nativeElement.getBoundingClientRect().width - margin.left - margin.right; const groupNames = this.data.map(item => item.name); const groupLabels = this.data.length > 0 ? this.data[0].series.map(item => item.name) : []; const xScale = this.getBandScale(groupNames, [0, width], this.innerPadding, this.outerPadding).round(true); const x1Scale = this.getBandScale(groupLabels, [0, xScale.bandwidth()], this.seriesInnerPadding, this.outerPadding).round(true); let chartContainer = this.svg.selectAll<SVGGElement, number>('g.chart-container').data([1]); chartContainer = chartContainer.enter() .append('g') .attr('class', 'chart-container') .merge(chartContainer) .attr('transform', `translate(${margin.left}, ${margin.right})`); let chartWrap = chartContainer.selectAll<SVGGElement, number>('g.chart-wrap').data([1]); chartWrap = chartWrap.enter() .append('g') .attr('class', 'chart-wrap') .merge(chartWrap) .attr('transform', 'translate(0, 0)'); const xAxis = chartWrap.selectAll<SVGGElement, number>('g.x-axis').data([1]); xAxis.enter() .append('g') .attr('class', 'x-axis') .merge(xAxis) .attr('transform', `translate(0, ${height})`) .call(d3.axisBottom(xScale)).selectAll('text') .style('text-anchor', 'middle'); const y = d3.scaleLinear().domain(this.domain).nice().rangeRound([height, 0]); let barWrap = chartWrap.selectAll<SVGGElement, number>('g.bar-wrap').data([1]); barWrap.exit().remove(); barWrap = barWrap.enter().append('g') .attr('class', 'bar-wrap') .merge(barWrap); let barGroup = barWrap.selectAll<SVGGElement, {name: string, series: {name: string, value: number}}>('g.bar-group').data(this.data); barGroup.exit().remove(); barGroup = barGroup.enter().append('g') .attr('class', 'bar-group') .merge(barGroup) .attr('transform', d => `translate(${xScale(d.name)}, 0)`); let barRects = barGroup.selectAll<SVGRectElement, {name: string, value: number}>('rect.bar').data(d => d.series.map(item => item)); barRects.enter() .append('rect') .merge(barRects) .attr('class', 'bar') .attr('width', x1Scale.bandwidth()) .attr('height', d => height - y(d.value)) .attr('x', (d: any) => x1Scale(d.name)) .attr('y', d => y(d.value)) .attr('fill', (d, i) => this.barColors[i]); let yAxis = chartWrap.selectAll<SVGGElement, number>('g.y-axis').data([1]); yAxis.enter() .append('g') .attr('class', 'y-axis') .merge(yAxis) .call(d3.axisLeft(y)); } } } |
This is it for d3 angular example tutorial. I hope you can use it similar concept to create the container for other charts like pie, bubble plot and many others. If you have any problem contact me through my contact form in this website.
Leave a Reply