Reputation: 147
so here is the thing, I'm trying to make work this example http://bl.ocks.org/mbostock/1093130 so the "update" function (that will be called every time I push a button on the DOM) re-draw the graph with a whole new different dataset... I made some modifications to the example to try this. So suppose I want to feed the graph randomly with different json's which names are stored in an array:
var array = ["graph.json","graph2.json"];
function reload() {
var rnd = Math.floor(Math.random()*2);
d3.json(array[rnd], function(error, json) {
if (error) throw error;
root = json;
update();
});
}
the thing is that when the graph is re-drew it always has some kind of bug, like some node that belongs to the previous data-set, or stuff like that... so first I tried removing elements from the container in every possible way I've seen here in SO before making the call to update() and that didn't work, so (correct me if I'm wrong) I read a little and found that the data() function uses some kind of join approach so you can keep updating the graph with every change registered in the data or something like that, which is cool but apparently not what I need. So I tried changing the data() by datum() because I read somewhere that the last should be used if you are not looking to dynamically update your layout, and I removed the enter() and exit() calls since I read they are not needed because the datum does not compute such functions. The code compiles but does not work, nothing is drew. Here is what I got (I put only the update() function because everything else didn't change):
function update() {
var nodes = flatten(root),
links = d3.layout.tree().links(nodes);
// Restart the force layout.
force
.nodes(nodes)
.links(links)
.start();
// Update links.
link = link.datum(links, function(d) { return d.target.id; });
link.insert("line", ".node")
.attr("class", "link");
// Update nodes.
node = node.datum(nodes, function(d) { return d.id; });
var nodeEnter = node.append("g")
.attr("class", "node")
.on("click", click)
.call(force.drag);
nodeEnter.append("circle")
.attr("r", function(d) { return Math.sqrt(d.size) / 10 || 4.5; });
nodeEnter.append("text")
.attr("dy", ".35em")
.text(function(d) { return d.name; });
node.select("circle")
.style("fill", color);
}
Thank you in advance for any help.
As you can see in the image above the data being showed does not correspond with the dataset, it should show names like "Mike", "Mike Jr.", "Paul"... and as I pointed out before if you try to retract/collapse the nodes clicking the root node some of the data is corrected (not the data on the root node).
Here is the data that should be showing:
//Second graph
{
"name": "Mike",
"children": [
{
"name": "Mike Jr.",
"children": [
{
"name": "Paul",
"children": [
{"name": "Peter", "size": 743}
]
}
]
}
]
}
Upvotes: 0
Views: 105
Reputation: 34549
So you may be missing some basics in D3
which is the enter
, update
, exit
pattern. Here's an article from Mike Bostock explaining the subject, and I've included an image from the post below:
The basic idea is that when you want to modify your data, you need to join your data again:
var circles= d3.selectAll("circle").data(nodes, function(d) { return d.id; });
and then use the relevant functions to determine what's changed. D3
under the hood keeps track of your data, and the HTML elements and works out what needs to be changed. You can therefore do stuff like this:
circles.enter().append("circle"); // Add circles for new items
circles.exit().remove(); // Remove old circles from the DOM as there's no data for them anymore
Note that once you've called enter()
, those data items have already been moved to the update
section - i.e. data points that already have a representation in the DOM. This means you can now do:
circles.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
I should note on the end however, that passing the key
function into .datum()
or .data()
is also important. I notice that you've got that already, you almost certainly need to keep it in there.
Upvotes: 1