Andrew Staroscik
Andrew Staroscik

Reputation: 2704

Parameter no of type node error with nested data in d3

I know there a are a bunch of D3 nesting question and answers here, but I can't seem to figure out what is going wrong with mine.

I created the following gist a while back when I first was working to make sense of nesting:

http://bl.ocks.org/AndrewStaroscik/5686192. The data is in the parentElement variable:

var parentElement = [
  {name: "Path 1", startX: 25, startY: 75, segments: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]},
  {name: "Path 2", startX: 25, startY: 125, segments: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
];

The following script renders a series of line segments as expected:

var g = svg.selectAll(".parent")
        .data(parentElement);

var gEnter = g.enter()
        .append("g")
        .attr("class", "parent")
        .attr('transform', function(d) { return 'translate(' + d.startX + ',' + d.startY + ')'; })
        .style('fill', 'none')
        .style('stroke', '#232323');

gEnter.selectAll(".child")
.data(function(d) { 
    console.log(d.segments);
    return d.segments; })
    .enter()
    .append("line")
    .attr('class', 'child')
    .attr('x1', function(d,i) { return lineLength * d; })
    .attr('y1', 0)
    .attr('x2', function(d,i) { return lineLength * (1 + d); })
    .attr('y2', 0);

Now I am trying to extend this by using an array of objects in the nest rather than an array of numbers. I modified the data as follows:

var parentElement = [
{name: "Path 1", startX: 25, startY: 75, segments: [
    {type: "line", val: 1},
    {type: "line", val: 2},
    {type: "line", val: 3},
    {type: "line", val: 4},
    {type: "line", val: 5},
    {type: "line", val: 6},
    {type: "line", val: 7},
    {type: "line", val: 8},
    {type: "line", val: 9}
    ]},
{name: "Path 2", startX: 25, startY: 125, segments: [
    {type: "line", val: 1},
    {type: "line", val: 2},
    {type: "line", val: 3},
    {type: "line", val: 4},
    {type: "line", val: 5},
    {type: "line", val: 6},
    {type: "line", val: 7},
    {type: "line", val: 8},
    {type: "line", val: 9}
    ]},
];

And the selectAll child part as follows:

gEnter.selectAll(".child")
.data(function(d) { 
    console.dir(d.segments);
    return d.segments; })
    .enter()
    .append(function(d) { 
        console.log(d.type);
        return d.type; 
    })
    .attr('class', 'child')
    .attr('x1', function(d,i) { return lineLength * d.val; })
    .attr('y1', 0)
    .attr('x2', function(d,i) { return lineLength * (1 + d.val); })
    .attr('y2', 0);

The ultimate goal is to be able to add multiple elements of different types (circles, squares, paths etc) to a "g" element using the same code. So the

.append(function(d) { 
  console.log(d.type);
  return d.type; 
})

is clearly unnecessary in this example, but will be important eventually.

Here is the complete nonfunctioning version: http://bl.ocks.org/AndrewStaroscik/e394056440e603374404

How the change in the data structure break the code?

Upvotes: 0

Views: 47

Answers (1)

Lars Kotthoff
Lars Kotthoff

Reputation: 109232

The problem is that .append() doesn't take a function that returns the element name -- you must either specify the element name as a string or return the DOM element from the function. So in your case, this would look like this:

.append(function(d) {
  return document.createElementNS(d3.ns.prefix.svg, d.type);
})

While this does make it work in your case, doing this in general would also require you to specify all the attributes in the data as different types of element have different attributes. This could make your code and data quite messy and introduce hard-to-debug bugs when the wrong kind of data is specified.

It's usually easier and safer to stick with the standard D3 approach. You can achieve the same thing by filtering the data before passing it to .data():

gEnter.selectAll("line.child")
  .data(function(d) {
    return d.segments.filter(function(e) { return e.type == "line"; });
  })
  .enter()
  .append("line")

and similarly for other types of elements.

Upvotes: 1

Related Questions