At the moment, I am attempting to create a Hierarchical Bar Chart in my Angular App using D3. When I click on a bar, I expect my function to recursively reshape the chart. The initial call works fine, but once I click on a bar, the variables become undefined.
After clicking on a bar, the updateChart function is triggered. However, this leads to an error being displayed in the console:
I attempted to store the variables in an object and import them. I also tried to assign them a value in the constructor, following the example I found here: Typescript: getting an undefined value , but nothing seems to resolve the issue.
The code I'm working with is based on a variation of this plunker: https://stackblitz.com/edit/angular-d3-v4-barchart?file=app%2Fshared%2Fbarchart%2Fbarchart.component.ts
As a newcomer to this, my code likely contains numerous mistakes, but the main hurdle I'm facing is utilizing the variables in the updateChart function.
import { Component, OnInit, ViewChild, ElementRef, Input, ViewEncapsulation } from '@angular/core';
import * as d3 from 'd3';
import { IHierarchyDatum } from './IHierarchyDatum';
import { HierarchyNode } from 'd3-hierarchy';
import { Agp } from '../agp';
@Component({
selector: 'app-d3-agp',
templateUrl: './d3-agp.component.html',
styleUrls: ['./d3-agp.component.css'],
encapsulation: ViewEncapsulation.None
})
export class D3AgpComponent implements OnInit {
@ViewChild('chart') private chartContainer: ElementRef;
private margin: any = { top: 20, bottom: 20, left: 20, right: 20 };
private chart: any;
private width: number;
private height: number;
private xScale: any;
private yScale: any;
private colors: any;
private xAxis: any;
private yAxis: any;
help: IHierarchyDatum;
constructor (public agp: Agp) {
}
ngOnInit () {
d3.json('http://localhost:3000/posts')
.then((data: IHierarchyDatum) => {
if (data) {
this.help = data;
this.createChart();
}
})
.catch((err) => {
console.log(err);
});
}
createChart () {
let root = d3.hierarchy(this.help[0], function (d) {
return d.children;
});
root = root.sum(function (d) {
return d.size;
});
const element = this.chartContainer.nativeElement;
this.width = element.offsetWidth - this.margin.left - this.margin.right;
this.height = element.offsetHeight - this.margin.top - this.margin.bottom;
const svg = d3.select(element).append('svg')
.attr('width', element.offsetWidth)
.attr('height', element.offsetHeight);
this.chart = svg.append('g')
.attr('class', 'bars')
.attr('transform', `translate(${this.margin.left}, ${this.margin.top})`);
const xDomain = root.children.map(d => d.data.name);
const yDomain = [0, d3.max(root.children, d => d.value)];
this.xScale = d3.scaleBand().padding(0.1).domain(xDomain).rangeRound([0, this.width]);
this.yScale = d3.scaleLinear().domain(yDomain).range([this.height, 0]);
this.colors = d3.scaleLinear().domain([0, root.children.length]).range(<any[]>['red', 'blue']);
this.xAxis = svg.append('g')
.attr('class', 'xAxis')
.attr('transform', `translate(${this.margin.left}, ${this.margin.top + this.height})`)
.call(d3.axisBottom(this.xScale));
this.yAxis = svg.append('g')
.attr('class', 'yAxis')
.attr('transform', `translate(${this.margin.left}, ${this.margin.top})`)
.call(d3.axisRight(this.yScale));
this.updateChart(root);
}
updateChart (root: HierarchyNode<IHierarchyDatum>) {
this.xScale.domain(root.children.map(d => d.data.name));
this.yScale.domain([0, d3.max(root.children, d => d.value)]);
this.colors.domain([0, root.children.length]);
this.xAxis.transition().call(d3.axisBottom(this.xScale));
this.yAxis.transition().call(d3.axisLeft(this.yScale));
const update = this.chart.selectAll('.bar')
.data(root.children)
.enter()
.append('rect')
.attr('class', 'bar')
.attr('x', d => this.xScale(d.data.name))
.attr('y', d => this.yScale(0))
.attr('width', this.xScale.bandwidth())
.attr('height', 0)
.style('fill', (d, i) => this.colors(i))
.transition()
.delay((d, i) => i * 10)
.attr('y', d => this.yScale(d.value))
.attr('height', d => this.height - this.yScale(d.value));
this.chart.selectAll('rect')
.on('click', this.updateChart);
}
}