DEV Community

Joshua
Joshua

Posted on • Updated on

<rect> attribute height: Expected length "NaN"

I have the following data in a csv file in my Angular project that also imports the D3.js library:

group,Nitrogen,normal,stress
banana,12,1,13
poacee,6,6,33
sorgho,11,28,12
triticum,19,6,1
Enter fullscreen mode Exit fullscreen mode

I have created a stacked-bar component with the following code in its html script:

<h3>Stacked Bar Chart</h3>
<figure id="stacked-bar"></figure>
Enter fullscreen mode Exit fullscreen mode

I also have the following code in the typescript file to display the stacked bar:

import { Component, OnInit } from '@angular/core';
import * as d3 from 'd3';

@Component({
  selector: 'app-stacked-bar',
  templateUrl: './stacked-bar.component.html',
  styleUrls: ['./stacked-bar.component.css']
})
export class StackedBarComponent implements OnInit {

  data = [
    {"group": "banana", "Nitrogen": "12", "normal": "1", "stress": "13"},
    {"group": "poacee", "Nitrogen": "6", "normal": "6", "stress": "33"},
    {"group": "sorgho", "Nitrogen": "11", "normal": "28", "stress": "12"},
    {"group": "triticum", "Nitrogen": "19", "normal": "6", "stress": "1"}
  ];

  svg: any;

  margin = 50;
  width = 750 - (this.margin * 2);
  height = 400 - (this.margin * 2);

  ngOnInit(): void {

    this.createSvg();
    this.drawBars(this.data);
  }

  createSvg(): void {

    this.svg = d3.select("figure#stacked-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 + ")");
  }

  drawBars(data): void {

    // List of subgroups - Header of the csv file - Here, soil condition.
    const subgroups = data.slice(1);
    console.log(subgroups);

    // List of groups - Value of the first column called group - Shown on the X axis - Here, species.
    const groups = data.map(d => (d.group));

    // Create the X-axis band scale.
    const x = d3.scaleBand()
    .domain(groups)
    .range([0, this.width])
    //.domain(data.map(d => d.groups))
    .padding(0.2);

    // Draw the X-axis on the DOM.
    this.svg.append("g")
    .attr("transform", "translate(0," + this.height + ")")
    .call(d3.axisBottom(x).tickSizeOuter(0));

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

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

    // color palette = one color per subgroup
    const color = d3.scaleOrdinal()
    .domain(subgroups)
    .range(['#e41a1c','#377eb8','#4daf4a']);

    // Stack the data per subgroup.
    const stackedData = d3.stack()
    .keys(subgroups)
    (data);

    // Create and fill the bars.
    this.svg.append("g")
    .selectAll("g")
    .data(stackedData)
    .join("g")
    .attr("fill", d => color(d.key))
    .selectAll("rect")    
    .data(d => d)
    .join("rect")
    .attr("x", d => x(d.data.group))
    .attr("y", d => y(d[1]))
    .attr("height", d => y(d[0]) - y(d[1]))
    .attr("width", x.bandwidth());
  }
}
Enter fullscreen mode Exit fullscreen mode

But I only get an empty plot! When I inspect the page, I have this console error:

attribute height: Expected length, "NaN"

It takes me to attr.js.31 where the line is else this.setAttribute(name, v).

It is actually pointing to .attr("height", d => y(d[0]) - y(d[1])). I don't see anything wrong with this line...

Can somebody please help me with this? I've been stuck in it for a few days now...

Top comments (1)

Collapse
 
stevereid profile image
Steve Reid

Did you manage to resolve this?