Reputation: 185
I've got a Django View feeding a tablib CSV to D3.js. However, I'm finding that no matter how or where I use exit(), it isn't returning anything- and thusly, I cannot remove the old, unneeded elements, and everything just overlays on top of each other.
Any ideas? I based my code pretty hard off of the basic Stacked Bar Chart example.
<div id="id_d3_canvas" class="d3_canvas_space">
</div>
<div>
<form id="id_date_form">
<input id="id_date_small" name="date1" type="text" value="03-01-2012">
<input id="id_date_large" name="date2" type="text" value="{% now 'n-j-Y' %}">
<select name="our_people" multiple>
{% for person in object_list %}
<option value="{{ person.name }}" selected>{{ person.name }}</option>
{% endfor %}
</select>
</form>
<button id="id_test_data_gather" class="btn btn-primary update_d3_csv">Update</button>
</div>
<script src="{{ STATIC_URL }}cms/js/d3.min.js"></script>
<script>
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 900 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.rangeRound([height, 0]);
var color = d3.scale.ordinal()
.range(["#98abc5", "#ff8c00"]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
var svg = d3.select(".d3_canvas_space").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var date_info = $("#id_date_form").serialize(),
initial_csv_url = "{% url blahblah %}?" + date_info;
function updateMultiData(multi_csv_url) {
d3.csv(multi_csv_url, function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "Name"; }));
data.forEach(function(d) {
var y0 = 0;
d.tasks = color.domain().map(function(category) { return {category: category, y0: y0, y1: y0 += +d[category]};});
d.total_tasks = d.tasks[d.tasks.length - 1].y1;
});
data.sort(function(a, b) { return b.total - a.total; });
x.domain(data.map(function(d) { return d.Name; }));
y.domain([0, d3.max(data, function(d) { return d.total_tasks; })]);
svg.selectAll(".name").data(data).exit().remove()
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("Tasks");
var person_name = svg.selectAll(".name")
.data(data)
.enter().append("g")
.attr("transform", function(d) { return "translate(" + x(d.Name) + ",0)"; });
person_name.selectAll("rect")
.data(function(d) { return d.tasks; })
.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.category); });
console.log(svg.selectAll(".name").data(data).exit());
var legend = svg.selectAll(".legend")
.data(color.domain().slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35m")
.style("text-anchor", "end")
.text(function(d) { return d; });
});
};
updateMultiData(initial_csv_url);
$(document).on("click", "button.update_d3_csv", function(e){
e.preventDefault();
var new_info = $("#id_date_form").serialize(),
new_multi_csv_url = "{% url blahblah %}?" + new_info;
updateMultiData(new_multi_csv_url);
console.log(new_multi_csv_url);
});
</script>
Upvotes: 1
Views: 2517
Reputation: 2206
In your example above it seems that the entered nodes have no defined class. Any subsequent d3.selectAll(".name")
will return an empty selection and all data elements will show up under .enter()
method.
You might want to try assigning the corresponding classname every time entering nodes are appended:
.enter().append("g").classed("name",true)
You might also want to consider using the second argument of .data()
to define a unique identifier (key) for each datapoint, ensuring that correct elements are exited on each update, if the order is different.
https://github.com/mbostock/d3/wiki/Selections#wiki-data
In your code "name" property could probably be used as a key:
var person_name = svg.selectAll(".name")
.data(data,function(d) { return d.name; })
Finally I notice that you are appending the axes inside the update function. This means a new set of axes will be appended on every update on top of the previous ones. You might want to move those out to the top level.
Upvotes: 3