user3727044
user3727044

Reputation: 11

Adding element to array in d3

I'm creating a webpage that will make a d3 force-layout graph using data from a JSON file, and when a button is pushed the user will be prompted for a name, and a new node will be created with the provided name. The page loads successfully and when the button is pushed an alert asks for a name, but when a name is entered there is an error that says 'Uncaught TypeError: undefined is not a function' and a new node isn't added.

What can I do to fix this error? Using Chrome I know the error is happening in the line where I try to append newNode. Here's my code:

<button onclick="addNode()">Click to add Node</button>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>

function addNode() {
  var nodeName = prompt("Name of new node:");
  var newGroup = Math.floor(Math.random() * 6);
  if (nodeName != null) {
    var newNode = {"node":{"name":nodeName,"group":newGroup}};
    force.nodes.append(newNode);
    force.start();
  }
}

var width = 960,
    height = 500;

var color = d3.scale.category20();

var force = d3.layout.force()
    .charge(-120)
    .linkDistance(30)
    .size([width, height]);

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

d3.json("user_interactions(1).json", function(error, graph) {
  force
      .nodes(graph.nodes)
      .links(graph.links)
      .start();

  var link = svg.selectAll(".link")
      .data(graph.links)
      .enter().append("line")
      .attr("class", "link")
      .style("stroke-width", function(d) { return Math.sqrt(d.value); });

  var node = svg.selectAll(".node")
      .data(graph.nodes)
      .enter().append("circle")
      .attr("class", "node")
      .attr("r", 5)
      .style("fill", function(d) { return color(d.group); })
      .call(force.drag);

  node.append("title")
      .text(function(d) { return d.name; });

  force.on("tick", function() {
    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; });

    node.attr("cx", function(d) { return d.x; })
    .attr("cy", function(d) { return d.y; });
  });
});

</script>

Upvotes: 1

Views: 2310

Answers (1)

Stephen Thomas
Stephen Thomas

Reputation: 14053

force.nodes is a function that returns the array of nodes. It's not a property. So force.nodes.append doesn't make any sense. You probably want to add the new node to the data,

force.nodes(force.nodes().push(newNode));

I'm not certain, however, that D3 will gracefully handle changing the nodes array dynamically.

In any case you'll have to add SVG elements for the new node and specify their attributes in a manner similar to how you're setting the initial properties (r, fill, etc.)

Upvotes: 1

Related Questions