2013-09-16 2 views
20

Я не могу решить, как лучше всего передать изменения в данные, которые происходят у родительского узла (например, элемент SVG g) до его дочерних элементов (например, SVG circle элементов) ,d3js: сделать новые родительские данные спускаться в дочерние узлы

Я читал this и this, но все еще не могу понять это.

Вот минимальный рабочий пример. В примере предполагается, что у вас есть объект с именем 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 содержит много circle s.

ответ

29

Есть 2 способа распространяющихся данных от родителей к детям:

  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]; }); 
    

Они составляют одно и то же. Первый вариант зависит от какого-то особого поведения при выборе, поэтому сначала может быть немного удивительно, но хорошо, что он делает шаблон для обновления узла симметричным с шаблоном для создания узла через insert/append. Второй вариант полезен, если вам необходимо применить какие-либо изменения к данным по мере их распространения.

Вот еще одна статья, которую вы не ссылаются на что может быть полезно также: Thinking with Joins

+0

Спасибо за комментарии. Эти работы, но только для первого элемента в каждом 'g'. Если я добавлю два круга в каждую группу, только новые выбранные круги будут обновлены новыми данными. Любая идея, почему это может быть? – LondonRob

+0

Если у вас есть более одного круга, вам нужно либо обрабатывать каждый отдельно, классифицируя их по-разному друг от друга, а затем делать select («circle.className») (или аналогичную идею для второго подхода), чтобы получить доступ к каждому из них. Если у вас много кругов в каждом g или переменном номере, вы можете использовать полное объединение данных с вложенными данными для создания/удаления окружностей. –

+0

Это не кажется таким сумасшедшим вариантом использования, что одна часть данных представлена ​​более чем одним элементом SVG. Стыдно, что у нас нет такого API! – LondonRob

1

Не уверен, что если вы поняли это, но это, безусловно, не в документации. Вся документация, касающаяся группировки элементов, по-видимому, не связана с выбором детей и наследованием данных для детей.

Ответ заключается в использовании конструкции .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]

Смежные вопросы