lambo_escrow
lambo_escrow

Reputation: 21

Data not being bound to a selection in d3

I am having an issue with binding data where the data is being bound to certain selections but not others.

For context, I have a series of rows in a csv file. Using d3, I iterate over each row and generate an svg for each row in the csv file. As demonstrated below.

var svgArea = d3.select("body").append("svg")
.attr("class", "barSvg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");

I then intend to use this selection to bind a <g> element and series of <rect> elements to the svg selection:

//creating a <g> elements
var vehicleText = svgArea.select("g")
.data(d)
.enter()
.append("g")
.attr("id", function(d) {return "vehicle_" + d.vehicle});

//creating a grouped bar chart
var vehicleBars = svgArea.selectAll("rect")
.data(d.sales)
.enter()
.append("rect")
.attr("class", "vehicleBars")
.attr("id", function (d) {return "vehicle_" + d.vehicle + "_" + d.year})
.attr("width", width / 4)
.attr( "x", function (d){
  return xYears(d.year)
})
.attr("y", function(d){
  return yScale(d.value)
})
.attr("height", function(d) {return height - yScale(d.value)})
.attr("fill", function(d) {return colorScale(d.year)});

The issue is that the <g> element doesn't appear however the <rect> elements do despite using the exact same selection. I have been thinking that this is might be an issue with the way I am binding data to the elements. What exactly am I doing wrong here? Why does it bind the data for the one set of elements but not the set which proceeds it? For reference, here is my code

Upvotes: 2

Views: 166

Answers (2)

Gerardo Furtado
Gerardo Furtado

Reputation: 102218

You have two main problems here:

  1. There are already <g> elements in the svg. So, instead of select("g"), your enter selection should select something that doesn't exist, like selectAll("foo").

  2. d is an object. However, the data function only accepts three things: an array, a function or nothing. So, it should be data([d]).

This problem #2 explains why, as you said...

the data is being bound to certain selections but not others.

... and why your console.log(vehicleText.datum()) returns Cannot read property '__data__' of null

That being said, here is your updated plunker: https://plnkr.co/edit/igliOTB1eq1k31eQ9nY5?p=preview

This, however, is not the best way to achieve what you want, nor the way I'd do it myself: have in mind that here I'm only answering your question, and nothing more.

PS: don't use d as the parameter for the data in your chartGenerator function. In D3 codes, d is usually used for the first parameter (the datum) in anonymous functions. Using d as you did will add some confusion, specially for seasoned D3 coders.

Upvotes: 1

Gilsha
Gilsha

Reputation: 14589

You can directly set the properties to the label texts. Try this way.

  var vehicleText = svgArea.append("g")
    .attr("id", "vehicle_" + d.vehicle); 

  vehicleText.append("text")
    .attr("class", "annotation")
    .attr("dx","4em")
    .attr("dy",height+20)
    .text( d.vehicle);

var rows = [{
    "vehicle": "Ford F - Series PU",
    "Mar-2016": 65179,
    "Mar-2017": 72089,
    "perc_change": 10.6
  },
  {
    "vehicle": "Chevrolet Silverado PU",
    "Mar-2016": 45009,
    "Mar-2017": 45280,
    "perc_change": 0.6
  },
  {
    "vehicle": "Dodge Ram PU",
    "Mar-2016": 34152,
    "Mar-2017": 36885,
    "perc_change": 8
  },
  {
    "vehicle": "Toyota Camry",
    "Mar-2016": 30942,
    "Mar-2017": 28189,
    "perc_change": -8.9
  },
  {
    "vehicle": "Toyota RAV4",
    "Mar-2016": 27376,
    "Mar-2017": 28116,
    "perc_change": 2.7
  },
  {
    "vehicle": "Honda Accord",
    "Mar-2016": 25571,
    "Mar-2017": 27182,
    "perc_change": 6.3
  },
  {
    "vehicle": "Toyota Corolla / Matrix",
    "Mar-2016": 24183,
    "Mar-2017": 26747,
    "perc_change": 10.6
  },
  {
    "vehicle": "Nissan Rogue",
    "Mar-2016": 22566,
    "Mar-2017": 26629,
    "perc_change": 18
  },
  {
    "vehicle": "Honda CR-V",
    "Mar-2016": 25939,
    "Mar-2017": 25758,
    "perc_change": -0.7
  },
  {
    "vehicle": "Honda Civic",
    "Mar-2016": 25052,
    "Mar-2017": 25303,
    "perc_change": 1
  },
  {
    "vehicle": "Ford Escape",
    "Mar-2016": 20806,
    "Mar-2017": 23012,
    "perc_change": 10.6
  },
  {
    "vehicle": "Chevrolet Equinox",
    "Mar-2016": 19636,
    "Mar-2017": 21600,
    "perc_change": 10
  },
  {
    "vehicle": "Toyota Highlander",
    "Mar-2016": 12742,
    "Mar-2017": 21241,
    "perc_change": 66.7
  },
  {
    "vehicle": "Nissan Altima",
    "Mar-2016": 20573,
    "Mar-2017": 20039,
    "perc_change": -2.6
  },
  {
    "vehicle": "Ford Explorer",
    "Mar-2016": 16690,
    "Mar-2017": 19628,
    "perc_change": 17.6
  },
  {
    "vehicle": "GMC Sierra PU",
    "Mar-2016": 16520,
    "Mar-2017": 18900,
    "perc_change": 14.4
  },
  {
    "vehicle": "Chevrolet Malibu",
    "Mar-2016": 10813,
    "Mar-2017": 18577,
    "perc_change": 71.8
  },
  {
    "vehicle": "Subaru Outback",
    "Mar-2016": 13075,
    "Mar-2017": 17769,
    "perc_change": 35.9
  },
  {
    "vehicle": "Ford Fusion",
    "Mar-2016": 19446,
    "Mar-2017": 17560,
    "perc_change": -9.7
  },
  {
    "vehicle": "Jeep Grand Cherokee",
    "Mar-2016": 17653,
    "Mar-2017": 17230,
    "perc_change": -2.4
  }
];


// Code goes here

//setting the margins
var margin = {
  top: 20,
  right: 20,
  bottom: 20,
  left: 20
}

//setting width and height
var width = 300 - margin.left - margin.right,
  height = 300 - margin.top - margin.bottom;

yearVals = ["Mar-2016", "Mar-2017"]

//ranges
var xVehicles = d3.scaleBand().rangeRound([0, width], .1).paddingInner(0.1);

var xYears = d3.scaleBand().domain(yearVals).range([0, xVehicles.bandwidth()])
  .paddingInner(.1)
  .paddingOuter(5.0);

var yScale = d3.scaleLinear().domain([0, 85000]).range([height, 0]);

var colorScale = d3.scaleOrdinal()
  .range(["#87ceeb", "#00bfff"]);


rows.forEach(function(data) {

  chartGenerator(data)
})

d3.selectAll("svg.barSvg")
  .data(rows)
  .enter()
  .append("text")
  .text(function(d) {
    return d.vehicle;
  })



function chartGenerator(d) {

  d.sales = yearVals.map(function(year) {
    return {
      "vehicle": d.vehicle,
      year: year,
      value: +d[year]
    }
  })

  //create vehicle name
  var vehicle = []
  vehicle["name"] = d.vehicle;


  var svgArea = d3.select("body")
    .append("svg")
    .attr("class", "barSvg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
    .append("g");

  //vehicleText not appended to g element


  //console.log(vehicleText.datum());

  var vehicleBars = svgArea.selectAll("rect")
    .data(d.sales)
    .enter()
    .append("rect")
    .attr("class", "vehicleBars")
    .attr("id", function(d) {
      return "vehicle_" + d.vehicle + "_" + d.year
    })
    .attr("width", width / 4)
    .attr("x", function(d) {
      return xYears(d.year)
    })
    .attr("y", function(d) {
      return yScale(d.value)
    })
    .attr("height", function(d) {
      return height - yScale(d.value)
    })
    .attr("fill", function(d) {
      return colorScale(d.year)
    });

  var vehicleText = svgArea.append("g")
    .attr("id", "vehicle_" + d.vehicle);

  vehicleText.append("text")
    .attr("class", "annotation")
    .attr("dx", width / 4)
    .attr("dy", height + 20)
    .text(d.vehicle);

}
/* Styles go here */

body {
  width: 1200px;
}

h2,
h3 {
  font-family: sans-serif;
  padding: 20px 50px 0px 50px;
}

.barSvg {
  background: white;
}

.vehicleData {
  background: grey;
  width: 25px;
  height: 15px;
}

.annotation {
  stroke: black;
  stroke-width: black;
}
<script src="https://d3js.org/d3.v4.min.js"></script>

<body>
  <h2>Auto Sales - March 2017</h2>
</body>

Upvotes: 1

Related Questions