d3js:使新的父数据下降到子节点

时间:2013-09-16 15:36:41

标签: d3.js

我无法弄清楚如何最好地将对父节点(例如SVG g元素)发生的数据的更改传递给它的子节点(例如SVG {{1元素)。

我已阅读thisthis,但仍无法弄清楚。

这是一个最低限度的工作示例。该示例假设您有一个名为circle的对象,该对象引用包含SVG元素的d3选择。

svg

任何人都可以帮我找到一个简洁的方法来将新名称放入一个组的所有子元素中吗?在我的现实示例中,每个data = [{"id":"A","name":"jim"},{"id":"B","name":"dave"},{"id":"C","name":"pete"}]; g = svg.selectAll("g").data(data, function(d) { return d.id; }).enter().append("g"); g.append("circle") .attr("r", 3) .attr("cx", 100) .attr("cy", function(d,i) {return 100 + (i * 30);}) // The data gets passed down to the circles (I think): console.log("circle data:"); d3.selectAll("g circle").each(function(d) { console.log(d.name); }); // Now change the data, and update the groups' data accordingly data = [{"id":"A","name":"carol"},{"id":"B","name":"diane"},{"id":"C","name":"susan"}]; svg.selectAll("g").data(data, function(d) { return d.id;}); // These are the results of the change: console.log("after change, the group has:"); d3.selectAll("g").each(function(d) { console.log(d.name); }); console.log("but the circles still have:"); d3.selectAll("g circle").each(function(d) { console.log(d.name); }); 包含许多g s。

2 个答案:

答案 0 :(得分:31)

有两种方法可以将数据从父母传播到儿童:

  1. selection.select会隐式执行此操作。 (selection.appendselection.insert的实现实际上基于selection.select内部)

    svg.selectAll("g").select("circle")
    
  2. 您可以使用函数显式重做数据连接以接收父数据并将其返回到子数组中。

    svg.selectAll("g").selectAll("circle")
        .data(function(d) { return [d]; });
    
  3. 这些相同的东西。第一个选项依赖于select中的一些特殊行为,因此它起初可能有点令人惊讶,但它的好处在于它使节点更新的模式与通过insert / append创建节点的模式对称。如果您需要在传播数据时对数据应用任何更改,则第二个选项很有用。

    以下是您未链接到的另一篇文章,可能也很有用:Thinking with Joins

答案 1 :(得分:2)

不确定你是否弄明白,但这绝对不在文档中。所有处理元素分组的文档似乎都没有处理子项选择和子项的数据继承。

答案是使用.each构造来更新子元素并将子元素附加到组enter()调用。

data = [{"id":"A","name":"jim"},{"id":"B","name":"dave"},{"id":"C","name":"pete"}];

function draw(data) {
  var g = svg.selectAll("g").data(data, function(d) { return d.id; })

  genter = g.enter().append("g");

  // This is the update of the circle elements - 
  // note that it is attached to the g data, not the enter()
  // This will update any circle elements that already exist
  g.each(function(d, i) {
    var group = d3.select(this);
    group.select("circle")
    .transition() 
      .attr("r", 3)
      .attr("cx", 100)
      .attr("cy", function(d,i) {return 100 + (i * 30);})
  }

  // The data DOES get passed down to the circles, and the enter() statement
  // will create a circle child for each data entry
  genter.append("circle")
      .attr("r", 3)
      .attr("cx", 100)
      .attr("cy", function(d,i) {return 100 + (i * 30);})
}

// Original drawing
draw(data);

// Now change the data, and update the groups' data accordingly
data = [{"id":"A","name":"carol"},{"id":"B","name":"diane"},{"id":"C","name":"susan"}];

// Second drawing, the SVG will be updated
draw(data);

如果有效,请告诉我。

[此答案基于此编码墙帖子中的一些代码分组:https://coderwall.com/p/xszhkg]