Reputation: 1153
I am trying to update data for a force simulation in D3 v4, similar to this unsuccessful attempt and semi-similar to this successful one. CodePen here
Instead of joining the new nodes it appears to be doubling all the nodes (see pictures). It does not seem to be refreshing the graph correctly either.
HTML:
<button onclick="addData()">Add Data</button>
<svg width="960" height="600"></svg>
JavaScript:
function addData() {
graph.nodes.push({"id": "Extra1", "group": 11},{"id": "Extra2", "group": 11})
graph.links.push({"source": "Extra1", "target": "Valjean", "strength": 1},{"source": "Extra1", "target": "Extra2", "strength": 2})
update()
simulation.restart()
}
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var link, linkEnter, nodeWrapper, nodeWrapperEnter;
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.id; }))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2))
.on("tick", ticked);
var allLinkG = svg.append("g")
.attr("class", "allLinkG")
var allNodeG = svg.append("g")
.attr("class", "allNodeG")
update()
simulation.restart()
function update(){
link = allLinkG
.selectAll("line")
.data(graph.links, function(d){ return d.id })
link.exit().remove()
linkEnter = link
.enter().append("line");
link = linkEnter.merge(link).attr("class","merged");
nodeWrapper = allNodeG
.selectAll("nodeWrapper")
.data(graph.nodes, function(d) { return d.id; })
nodeWrapper.exit().remove();
nodeWrapperEnter = nodeWrapper.enter()
.append("g").attr("class","nodeWrapper")
.append("circle")
.attr("r", 2.5)
nodeWrapper = nodeWrapperEnter
.merge(nodeWrapper).attr("class","merged");
simulation
.nodes(graph.nodes);
simulation.force("link")
.links(graph.links);
}
function ticked() {
link
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
nodeWrapper
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
Thanks very much for any help.
Upvotes: 1
Views: 2027
Reputation: 108512
A few issues here:
selectAll
is selecting by element type nodeWrapper
, you meant by class .nodeWrapper
..merge
, you can't do this as it breaks future selections by .nodeWrapper
class..merge
your selection, you are merging circle
s with g
s. You should stay consistent and operate on the g
s only.Quick refactor:
function update() {
link = allLinkG
.selectAll("line")
.data(graph.links, function(d) {
return d.id
})
link.exit().remove()
linkEnter = link
.enter().append("line");
link = linkEnter.merge(link).attr("class", "merged");
nodeWrapper = allNodeG
.selectAll(".nodeWrapper") //<-- class nodeWrapper
.data(graph.nodes, function(d) {
return d.id;
})
nodeWrapperEnter = nodeWrapper.enter()
.append("g").attr("class", "nodeWrapper"); //<-- enter selection should be gs
nodeWrapperEnter //<-- append your circles
.append("circle")
.attr("r", 2.5)
nodeWrapper = nodeWrapperEnter //<-- merge, don't change class
.merge(nodeWrapper);
nodeWrapper.exit().remove(); //<-- and the exit
simulation
.nodes(graph.nodes);
simulation.force("link")
.links(graph.links);
}
Note, I also modified your tick
function to operate on the g
instead of the circle
:
nodeWrapper
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
Full code is here.
Upvotes: 2