2014-05-07 65 views
1

我有一個相當簡單的可重複使用的圖表,內置D3.js - 一些圈子和一些文本。d3.js:爲什麼數據沒有更新?

我很努力地弄清楚如何在不重繪整個圖表的情況下使用新數據更新圖表。

使用當前腳本,我可以看到新數據綁定到svg元素,但沒有任何數據驅動的文本或屬性正在更新。 爲什麼不更新圖表?

這裏有一個小提琴:http://jsfiddle.net/rolfsf/em5kL/1/

我打電話這樣的圖表:

d3.select('#clusters') 
.datum({ 
    Name: 'Total Widgets', 
    Value: 224, 
    Clusters: [ 
     ['Other', 45], 
     ['FooBars', 30], 
     ['Foos', 50], 
     ['Bars', 124], 
     ['BarFoos', 0] 
    ] 
}) 
.call(clusterChart()); 

當單擊該按鈕時,我只是再次調用圖表,不同數據

$("#doSomething").on("click", function(){ 

    d3.select('#clusters') 
     .datum({ 
      Name: 'Total Widgets', 
      Value: 122, 
      Clusters: [ 
       ['Other', 14], 
       ['FooBars', 60], 
       ['Foos', 22], 
       ['Bars', 100], 
       ['BarFoos', 5] 
      ] 
     }) 
     .call(clusterChart()); 

}); 

圖表腳本:

function clusterChart() { 
var width = 450, 
    margin = 0, 
    radiusAll = 72, 
    maxRadius = radiusAll - 5, 
    r = d3.scale.linear(), 
    padding = 1, 
    height = 3 * (radiusAll*2 + padding), 
    startAngle = Math.PI/2, 
    onTotalMouseOver = null, 
    onTotalClick = null, 
    onClusterMouseOver = null, 
    onClusterClick = null; 
    val = function(d){return d}; 

function chart(selection) { 
    selection.each(function(data) { 

     var cx = width/2, 
      cy = height/2, 
      stepAngle = 2 * Math.PI/data.Clusters.length, 
      outerRadius = 2*radiusAll + padding; 

     r = d3.scale.linear() 
        .domain([0, d3.max(data.Clusters, function(d){return d[1];})]) 
        .range([50, maxRadius]); 

     var svg = d3.select(this).selectAll("svg") 
        .data([data]) 
        .enter().append("svg"); 

     //enter 
     var totalCircle = svg.append("circle") 
        .attr("class", "total-cluster") 
        .attr('cx', cx) 
        .attr('cy', cy) 
        .attr('r', radiusAll) 
        .on('mouseover', onTotalMouseOver) 
        .on('click', onTotalClick); 

     var totalName = svg.append("text") 
        .attr("class", "total-name") 
        .attr('x', cx) 
        .attr('y', cy + 16); 

     var totalValue = svg.append("text") 
        .attr("class", "total-value") 
        .attr('x', cx) 
        .attr('y', cy + 4); 



     var clusters = svg.selectAll('circle.cluster') 
        .data(data.Clusters) 
        .enter().append('circle') 
        .attr("class", "cluster"); 

     var clusterValues = svg.selectAll("text.cluster-value") 
        .data(data.Clusters) 
        .enter().append('text') 
        .attr('class', 'cluster-value'); 

     var clusterNames = svg.selectAll("text.cluster-name") 
        .data(data.Clusters) 
        .enter().append('text') 
        .attr('class', 'cluster-name'); 


     clusters .attr('cx', function(d, i) { return cx + Math.cos(startAngle + stepAngle * i) * outerRadius; }) 
        .attr('cy', function(d, i) { return cy + Math.sin(startAngle + stepAngle * i) * outerRadius; }) 
        .attr("r", "10") 
        .on('mouseover', function(d, i, j) { 
         if (onClusterMouseOver != null) onClusterMouseOver(d, i, j); 
        }) 
        .on('mouseout', function() { /*do something*/ }) 
        .on('click', function(d, i){ onClusterClick(d); }); 

     clusterNames 
        .attr('x', function(d, i) { return cx + Math.cos(startAngle + stepAngle * i) * outerRadius; }) 
        .attr('y', function(d, i) { return cy + Math.sin(startAngle + stepAngle * i) * outerRadius + 16; }); 

     clusterValues 
        .attr('x', function(d, i) { return cx + Math.cos(startAngle + stepAngle * i) * outerRadius; }) 
        .attr('y', function(d, i) { return cy + Math.sin(startAngle + stepAngle * i) * outerRadius - 4; }); 


     //update with data 
     svg   .selectAll('text.total-value') 
        .text(val(data.Value)); 

     svg   .selectAll('text.total-name') 
        .text(val(data.Name)); 

     clusters 
        .attr('class', function(d, i) { 
         if(d[1] === 0){ return 'cluster empty'} 
         else {return 'cluster'} 
        }) 
        .attr("r", function (d, i) { return r(d[1]); }); 

     clusterValues 
        .text(function(d) { return d[1] }); 

     clusterNames  
        .text(function(d, i) { return d[0] }); 


     $(window).resize(function() { 
      var w = $('.cluster-chart').width(); //make this more generic 
      svg.attr("width", w); 
      svg.attr("height", w * height/width); 
     }); 

    }); 


} 

chart.width = function(_) { 
    if (!arguments.length) return width; 
    width = _; 
    return chart; 
}; 

chart.onClusterClick = function(_) { 
    if (!arguments.length) return onClusterClick; 
    onClusterClick = _; 
    return chart; 
}; 

return chart; 
} 

回答

1

我已經使所有有關的SVG元素(包括SVG本身)施加的輸入/更新/出口圖案。下面是一個例子段:

var clusterValues = svg.selectAll("text.cluster-value") 
    .data(data.Clusters,function(d){ return d[1];}); 

clusterValues.exit().remove(); 

clusterValues 
    .enter().append('text') 
    .attr('class', 'cluster-value'); 
... 

下面是一個完整FIDDLE與工作的所有部件。

注意:我試着儘可能少地觸摸你的代碼,因爲你已經仔細地去了解一個可重用的方法。這就是輸入/更新/退出模式在總的圓圈(和文本)和其他圓圈(和文本)之間有點不同的原因。我可能會使用svg:g元素對每個圓圈和相關文本進行分組。

+0

非常感謝你@FernOfTheAndes花時間重新編寫小提琴並讓它工作。我開始嘗試將「進入」和「更新」分開,但從未完全理解「退出」。我同意svg:g元素 - 我原來使用它,但是當事情沒有奏效時,我就擺脫了它。再次感謝! – rolfsf

+0

關於svg:g方法的後續問題 - 如果我將總計元素放在他們自己的組中,我只需要在「g」上執行.exit.remove(),而不是每個元素(文本,圓) ? – rolfsf

+0

@rolfsf是的,非常。我繼續在我的年輕人中創建了一篇文章:) [博客](http://fernoftheandes.com/blog/)。如果解釋清楚,請讓我知道。謝謝! – FernOfTheAndes