Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

D3.js not updating data

I am updating a D3 stacked bar graph in combination with AngularJS. In this, all data is initially visible, and updates filter out undesired data. In the past it has worked without issue using this model:

data = [{
    name: John Doe, 
    splits: [{distance: 1, time: 1234},]
},...]

Now I am attempting to to add one more bar to each stack using this model:

data = [{
    name: John Doe
    time: 12345, 
    splits: [{distance: 1, time: 1234},]
},...]

My issue is updating the data. My calculated values are recalculated correctly, such as the domain for scaling. A line for time update still only recognizes the data values from before the update (code snippet heavily truncated for brevity):

// Set ranges of values in axes
x.domain(data.map(function(d) { return d.name}));
y.domain([ min , max]);

// Y Axis is updated to the correct time range
chart.append('g').attr('class', 'y axis').call(yAxis).selectAll('text').style('font-family','Open Sans').style('font-size', '.9rem'); 

// data join / Select Athlete
var athlete = chart.selectAll(".athlete").data(data),
athdata = athlete.enter(),

console.log(data) // data is correct here

// add container for each athlete
athplace = athdata.append("g")
  .attr("class", "athlete")
  .attr("transform", function(d) {  return "translate(" + x(d.name) + ",0)"; })
  .text(function(d) { return d.name}),

// ENTER time
athplace.append('rect')
.attr('class', "time")
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.time.time); })
.attr("height", 0).transition().duration(300)
.attr("height", function(d) {  return height-y(d.time); });

... enter splits & labels

// exit splits & athlete
splitlabels.exit().transition().duration(300).attr('opacity', 0).remove();
splits.exit().transition().duration(300).attr('height', 0).attr('opacity', 0).remove();
athlete.exit().transition().duration(300).attr('width',0).attr('opacity', 0).remove();

console.log(athlete) // data is still correct

// UPDATE time, this chain has the problem with data not being updated. "d" is equal to previous values
athlete.selectAll('rect.time')
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.time.time); })
.attr("height", function(d) {  return height-y(d.time); });

Due to these errors, the updated columns represent the wrong data and produce the wrong visualization. I have been staring/testing at this all day trying to isolate the issue to what I have now. Could someone more experienced with D3 give me some insight?

Note: for those interested, this is all in an Angular directive where I $watch for changes to the data values, although I am 100% sure that that is not the issue.

Edit

Here is a JSFiddle that illustrates the same error as in my script. All of the code was extracted directly from the script exhibiting the issue. The interval update at the bottom imitates the swapping of data that would normally happen.

like image 934
DFenstermacher Avatar asked Dec 22 '14 02:12

DFenstermacher


1 Answers

I played with your example for a little bit and made a few changes that may be of use.

First, I re-factored your global var's up top to inside your update function. By doing this, we are not "double-appending" where our x and y axis left off. This was seemingly drawing our chart on top of our old chart.

However, fixing this then gave me the new chart overlaying on top of our old. To fix this, I call d3.selectAll("svg > *").remove(); within update to remove all attached SVG elements and groupings, giving us a "clean slate" to render our new chart on. This solves the overlaying issue.

You mention this is shortened for brevity, so I hope this example can help with your actual application


Updated Fiddle

like image 178
scniro Avatar answered Nov 10 '22 08:11

scniro