25

我不知道如何最好地将发生在父节点(例如 SVGg元素)的数据更改传递给它的子节点(例如 SVGcircle元素)。

我读过这个这个,但仍然无法弄清楚。

这是一个最小的工作示例。该示例假定您有一个名为的对象,该对象svg引用包含 SVG 元素的 d3 选择。

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包含许多circles。

4

2 回答 2

33

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

  1. selection.select将隐式执行此操作。(selection.append和的实现selection.insert其实都是基于selection.select内部的)

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

    svg.selectAll("g").selectAll("circle")
        .data(function(d) { return [d]; });
    

这些相当于同一件事。第一个选项依赖于 select 中的一些特殊行为,因此一开始可能会有点令人惊讶,但它很好的是它使节点更新的模式与通过插入/追加创建节点的模式对称。如果您需要在数据传播时对数据应用任何更改,则第二个选项很有用。

这是您未链接到的另一篇文章,它也可能有用:用联接思考

于 2013-09-16T15:50:00.420 回答
3

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

答案是使用.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);

让我知道这个是否奏效。

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

于 2014-01-06T05:33:32.233 回答