-2

I'm trying to make a pie chart with d3.js that looks like this:

enter image description here

Note that the labels are placed along the edges of the pie chart. Initially, I am able to draw the pie charts and properly place the text nodes (the fiddle only displays one pie chart; assume, however, that they all have data that works and is appropriate, as this one does). However, when I go to adjust the data, I can't seem to .attr(translate, transform) them to the correct region along the edge of the pie chart (or do anything to them, for that matter):

changeFunctions[i] = function (data, i) {
    path.data(pie(data))
        .transition()
        .duration(750)
        .attrTween("d", arcTween);

    text.each(function (d, num) {
        d3.select(this)
            .text(function (t) {
                return t.data.name+". "+(100 * t.data.votes/totalVotes).toFixed(0) + "%";
            })
            /*
            .attr("transform", function (d) {
                //console.log("the d", d)
                var c = arc.centroid(d),
                    x = c[0], y = c[1],
                    h = Math.sqrt(x * x + y * y);
                return "translate(" + (x/h * 100) +  ',' + (y/h * 100) +  ")"; 
            })*/
            .attr("opacity", function (t) {
                return t.data.votes == 0 ? 0 : 1;
            });
    })
}

I have omitted the general code to draw the pie chart; it's in the jsfiddle. Basically, I draw each of the pie charts in a for loop and store this function, changeFunctions[i], in a closure, so that I have access to variables like path and text.

The path.data part of this function works; the pie chart properly adjusts its wedges. The text.each part, however, does not.

How should I go about making the text nodes update both their values and locations?

fiddle

Community
  • 1
  • 1
royhowie
  • 11,075
  • 14
  • 50
  • 67

1 Answers1

2

When updating the text elements, you also need to update the data that's bound to them, else nothing will happen. When you create the elements, you're binding the data to the g element that contains the arc segment and text. By then appending path and text, the data is "inherited" to those elements. You're exploiting this fact by referencing d when setting attributes for those elements.

Probably the best way to make it work is to use the same pattern on update. That is, instead of updating only the data bound to the path elements as you're doing at the moment, update the data for the g elements. Then you can .select() the descendant path and text elements, which will again inherit the new data to them. Then you can set the attributes in the usual manner.

This requires a few changes to your code. In particular, there should be a variable for the g element selection and not just for the paths to make things easier:

var g = svg.selectAll("g.arc")
        .data(pie(data));
g.enter()
        .append("g").attr("class", "arc");
var path = g.append("path");

The changeFunction code changes as follows:

var gnew = g.data(pie(data));
gnew.select("path")
            .transition()
            .duration(750)
            .attrTween("d", arcTween);

Now, to update the text, you just need to select it and reset the attributes:

gnew.select("text")
    .attr("transform", function(d) { ... });

Complete demo here.

Lars Kotthoff
  • 107,425
  • 16
  • 204
  • 204
  • I actually realized my problem last night (thought I closed the question—guess not); your answer is really good, though, and will definitely help future visitors, so it's good that you provided an answer. I ended up solving it [somewhat differently](http://plebvote.com/script/viewpoll.js) so that the [graphs](http://plebvote.com/3/view) could update live. I looped through all the different pie charts via a helper function, `makeGraphs`, and set them up individually. I returned a function so that I could still have access to `text` and `path` via a closure. – royhowie Aug 15 '14 at 21:06