How to update data in stack bar chart in D3

gunjan picture gunjan · Aug 12, 2013 · Viewed 8.3k times · Source

I am able to populate a stacked bar chart first time, but my requirement is to update the stacked bar chart with new data on button click. On button click, i m making call to backend and getting the data, Could you please guide me as how to update the stacked bar char chart. My problem is passing the new data to bar chart.

 d3.json("http://myhost/ITLabourEffortChart/effort/effort",function(error, data){
    color.domain(d3.keys(data.effort[0]).filter(function(key) { 
        return key !== "qName"; }));
data.effort.forEach(function(d) {
var y0 = 0;
d.effortHr = color.domain().map(function(name) { 
    return {name: name, y0: y0, y1: y0 += +d[name]}; });

d.total = d.effortHr[d.effortHr.length - 1].y1;


});
x.domain(data.effort.map(function(d) { return d.qName; }));
y.domain([0, d3.max(data.effort, function(d) { 
    return d.total; })]);

svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);

svg.append("g")
  .attr("class", "y axis")
  .call(yAxis)
.append("text")
  .attr("transform", "rotate(-90)")
  .attr("y", 6)
  .attr("dy", ".71em")
  .style("text-anchor", "end")
  .text("FTE");

var state = svg.selectAll(".layer")
  .data(data.effort)
.enter().append("g")
  .attr("class", "g")
  .attr("transform", function(d) { return "translate(" + x(d.qName) + ",0)"; });

 rect = state.selectAll("rect")
 .attr("id", "barchart") 
  .data(function(d) { 
      return d.effortHr; })
.enter().append("rect")
  .attr("width", x.rangeBand())
  .attr("y", function(d) { 
      return y(d.y1); })
  .attr("height", function(d) { return y(d.y0) - y(d.y1); })
  .style("fill", function(d) { return color(d.name); });


On Update i am calling below method
function redraw() {
    d3.json("http://localhost:8080/ITLabourEffortChart/effort/effort/YrReports",function(error, data){

        color.domain(d3.keys(data.effort[0]).filter(function(key) {

            return key !== "qName"; }));
    data.effort.forEach(function(d) {
    var y0 = 0;
    d.ages = color.domain().map(function(name) { 
        return {name: name, y0: y0, y1: y0 += +d[name]}; });
    d.total = d.ages[d.ages.length - 1].y1;
    });

     var updatebar = svg.selectAll("#barchart");
    // Update…
     updatebar
     .transition()
    .duration(500)
    .delay(function(d, i) { return i * 10; })
  .transition()
    .attr("width", x.rangeBand())
  .attr("y", function(d) { 
      return y(d.y1); })
  .attr("height", function(d) { return y(d.y0) - y(d.y1); })
  .style("fill", function(d) { return color(d.name); }
  .attr("x", function(d) { 
    return x(d.x); })

  );

    });
  .attr("x", function(d) { 
    return x(d.x); })

  );

    });

Answer

nothing9 picture nothing9 · Aug 12, 2013

To update your data you will just need to select the svg elements again and rebind the data. In your example you are already selecting the #barchart, now you just need to rebind the data. And you can do that in the same way you did it when you first created the svg Elements. So something like this should do the trick:

var updatebar = svg.selectAll("#barchart");
.data(newdata)
.transition()
.duration(500)
... (etc.)

Here you can find a more detailed explaination: http://chimera.labs.oreilly.com/books/1230000000345/ch09.html#_updating_data

Update:

Ok, unfortunately I cannot use Fiddle so I just post my working code here. As far as I could see you have a problem with your selectAll, because there is no element called .effort. Here is the updated code for your redraw-function:

function redraw() {

    var effort = [];
    var obj = {
        pfte: "20",
        efte: "50",
        qName: "Q1"
    };
    var obj2 = {
        pfte: "10",
        efte: "13",
        qName: "Q2"
    };

    effort[0] = obj;
    effort[1] = obj2;
    var newDataSet = new Object();
    newDataSet.effort = effort;

    color.domain(d3.keys(newDataSet.effort[0]).filter(function (key) {
        return key !== "qName";
    }));

    effortDataSet = newDataSet.effort;
    effortDataSet.forEach(function (d) {
        var y0 = 0;
        d.effortHr = color.domain().map(function (name) {
            return { name: name, y0: y0, y1: y0 += +d[name] };
        });
        d.total = d.effortHr[d.effortHr.length - 1].y1;
    });

    state = svg.selectAll(".g")
        .data(effortDataSet)
        .attr("class", "g")
        .attr("transform", function (d) { return "translate(" + x(d.qName) + ",0)"; });

    state = state.selectAll("rect")
        .data(function (d) {
            return d.effortHr;
        })
        .attr("width", x.rangeBand())
        .attr("y", function (d) {
            return y(d.y1);
        })
        .attr("height", function (d) {
        //console.log(y(d.y0) - y(d.y1));
        return y(d.y0) - y(d.y1);
        })
        .style("fill", function (d) { return color(d.name); });
}