My code draws an adjustable donut chart in an Angular project.
I, then, added a window resize event listener and handler, assuming that re-drawing and re-painting are all I need to do:
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { Item, DataService } from '../data.service';
import * as d3 from 'd3';
@Component({
selector: 'app-donut',
templateUrl: './donut.component.html',
styleUrls: ['./donut.component.css'],
})
export class DonutComponent implements OnInit {
svg: d3.Selection<SVGGElement, unknown, HTMLElement, any>;
margin = 50;
width = 750 - (this.margin * 2);
height = 400 - (this.margin * 2);
arc: any;
pie: any;
slices: any;
color: any;
radius: number;
mainContainer: any;
dataSource: Item[];
constructor(private dataService: DataService) { this.dataSource = this.dataService.getData(); }
ngOnInit() {
this.createSvg();
}
createSvg(): void {
this.svg = d3
.select("figure#donut")
.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 + ")");
this.pie = d3.pie().sort(null).value((d: any) => d.abs);
this.radius = (Math.min(this.width, this.height)) / 1.8;
this.color = d3.scaleOrdinal(d3.schemeCategory10);
this.mainContainer = this.svg
.append('g')
.attr('transform', `translate(${this.radius},${this.radius})`);
}
drawBars() {
this.setArcs();
this.drawSlices();
}
setArcs() {
// innerRadius(0) draws a pie chart; i.e. a donut chart without a hole!
this.arc = d3.arc().outerRadius(this.radius).innerRadius(this.radius * 0.5);
}
drawSlices() {
this.slices = this.mainContainer
.selectAll('path')
.remove().exit()
.data(this.pie(this.dataSource))
.enter().append('g').append('path')
.attr('d', this.arc);
this.slices
.attr('fill', (d, i) => this.color(i));
}
resize() {
this.createSvg();
this.drawBars();
}
}
This is the content of my DataService script:
import { Injectable } from '@angular/core';
export interface Item {
name: string;
value: number;
abs: number;
}
@Injectable({
providedIn: 'root'
})
export class DataService {
private readonly NAMES = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
private readonly MIN_ITEM = 10;
private readonly MAX_ITEM = 20;
private readonly MAX_VALUE = 100;
constructor() { }
private generateRandomValue(start: number, end: number) {
return Math.ceil(Math.random() * (end - start) + start);
}
getData(): Item[] {
const nbItems = this.generateRandomValue(this.MIN_ITEM, this.MAX_ITEM);
const samples = [];
for (let i = 0; i < nbItems; i++) {
const val = this.generateRandomValue(1, this.MAX_VALUE);
samples.push({
name: this.NAMES[i],
value: val,
abs: Math.abs(val)
});
}
return samples;
}
}
My CSS script:
.center {
display: flex;
justify-content: center;
}
My HTML script:
<h3 class="center">Donut Chart</h3>
<figure id="donut" class="center"></figure>
The only problem I am having is that the resize function does not work. As soon as I start dragging the edges, the chart breaks up and multiplies fast, until it disappears...
What am I doing wrong?