Search code examples
d3.jsbar-chartanimated

Reload nested data in D3.js


I do not manage to update a bar-chart with nested data in D3.js with new data.

I have nested data of the form:

data = [[1,2,3,4,5,6],[6,5,4,3,2,1]];

I managed to visualize the data by first appending a group for every subarray. In the groups I then add the arrays as data (simplified):

function createGraph(l, svg){
  var g = svg.selectAll("g")  
    .data(l)
    .enter().append("g");  

  var rect = g.selectAll("rect)  
    .data(function(d){return d;})  
    .enter().append("rect")
    . ...
}

However, when call the function again with different data, nothing happens.

It seems like in the second row, the rects do not get updated.

I have created a full example over at jsBin: http://jsbin.com/UfeCaGe/1/edit?js,output


Solution

  • A little more explanation of Lars' bug-catch, since I'd already started playing around...

    The key was in this section of the code:

      var group = svg.selectAll("g")  
        .data(l)
        .enter().append("g"); 
    

    The variable group is assigned the enter selection, not the raw selection. Then in the next line:

    var bar = group.selectAll("rect")
            .data(function(d){
                return d;
            }); 
    

    You end up defining bar as only the rectangles that are children of just-entered groups. So even though you were handling update correctly for the rectangles, that whole section of code wasn't even running. You need to save the group selection before branching the chain to deal with entering groups:

    var group = chart.selectAll("g")
            .data(dt);
    
    group.enter().append("g");
    
    var bar = group.selectAll("rect")
            .data(function(d){
                return d;
            }); 
    

    Also, you're missing a j in your function declaration in your update. And you can reduce code duplication by putting your rectangle update code after your rectangle enter code, and then any attributes that get set in the update don't have to be specified for enter. (Some older examples don't use this pattern, because the original versions of d3 didn't automatically transfer newly-entered elements to the main selection.)

    // enter
    bar.enter().append("rect")
        .attr("fill", function(d,i,j){
            return colors(j);})
        .attr("height", 0);
    
    // update
    bar.attr("transform", function(d, i, j) {
            x = "translate("+(i*2.2*w+j*w)+",0)";
            return x; })
        .transition()
        .duration(750)
        .attr("width", w)
        .attr("height", function(d){return d*10;});