简体   繁体   English

D3-如何更新树形布局中的隐藏分支

[英]D3 - How to update hidden branches in tree layout

I have a D3 tree layout with collapsible nodes, and I want to be able to update the links and link text with data that comes back from an ajax call. 我有一个带有可折叠节点的D3树布局,并且我希望能够更新链接并使用ajax调用返回的数据来链接文本。 I've mostly got it working, but if any of the nodes in the tree are collapsed I can't figure out how to update the "hidden" data, so it properly displays the new data if those branches are later reopened. 我大多数情况下都能正常工作,但是如果树中的任何节点都折叠了,我将无法弄清楚如何更新“隐藏”数据,因此,如果稍后重新打开这些分支,它将正确显示新数据。

The behavior might be clearer with this JSFiddle. 使用此JSFiddle,行为可能会更清楚。

https://jsfiddle.net/ddbz5kq1/6/ https://jsfiddle.net/ddbz5kq1/6/

// ************** Generate the tree diagram  *****************
var margin = {top: 0, right: 80, bottom: 20, left: 80};
var width = 850 - margin.right - margin.left;
var height = 1000 - margin.top - margin.bottom;
var i = 0;
var root;


// Exactly equal to our old var tree
var tree = d3.layout.tree()
    .size([height, width]);

// UNSURE, we did not have this before - before it was inside of function! //
var diagonal = d3.svg.diagonal()
    .projection(function(d) { return [d.y, d.x]; });

// Exactly qual to our old var canvas//
var svg = d3.select("body")
    .append("svg")
        .attr("width", width + margin.right + margin.left)
        .attr("height", height + margin.top + margin.bottom)
    .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

root = treeStructure;
root.x0 = height / 2;
root.y0 = 0;

update(root);

function update(source) {

  var duration = d3.event && d3.event.altKey ? 5000 : 500;

  // Compute the new tree layout.
  var nodes = tree.nodes(root).reverse();
  var links = tree.links(nodes);

  // Normalize for fixed-depth.
  nodes.forEach(function(d) { d.y = d.depth * 180; });

  // Declare the nodes…
  var node = svg.selectAll("g.node")
      .data(nodes, function(d) { return d.id || (d.id = ++i); });

  // Enter the nodes.
  var nodeEnter = node.enter()
      .append("g")
        .attr("class", "node")
        .attr("transform", function(d) { 
          return "translate(" + source.y0 + "," + source.x0 + ")"; })
        .on("click", function(d) { toggle(d); update(d); });

  nodeEnter.append("circle")
      .attr("r",5)
      .style("stroke", "black")
      .style("fill", "yellow");

  nodeEnter.append("text")
        .text(function (d) {
            return d.dName; })
        .attr("text-anchor", "middle")
        .attr("dy", "-2");

  // Transition nodes to their new position.
  var nodeUpdate = node.transition()
      .duration(duration)
      .attr("transform", function(d) { 
        return "translate(" + d.y + "," + d.x + ")"; });

  nodeUpdate.select("circle")
      .attr("r", 2.5)
      .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });

  nodeUpdate.select("text")
      .style("fill-opacity", 1);

  // Transition exiting nodes to the parent's new position.
  var nodeExit = node.exit().transition()
      .duration(duration)
      .attr("transform", function(d) { 
        return "translate(" + source.y + "," + source.x + ")"; })
      .remove();

  nodeExit.select("circle")
      .attr("r", 1e-6);

  nodeExit.select("text")
      .style("fill-opacity", 1e-6);

  // Declare the links…
  var link = svg.selectAll("path.link")
      .data(links, function(d) {
      return d.target.id; });

  // Enter the links.
  link.enter()
      .insert("path", "g")
      .attr("class", "link")
      .attr("stroke",function(d) {
        return d.target.color;})
       .attr("stroke-width",function(d) {
        return d.target.linkWidth;})
      .attr("d", function(d) {
        var o = {x: source.x0, y: source.y0};
        return diagonal({source: o, target: o});
      })

  // Transition links to their new position.
  link.transition()
      .duration(duration)
      .attr("d", diagonal);

  // Transition exiting nodes to the parent's new position.
  link.exit().transition()
      .duration(duration)
      .attr("d", function(d) {
        var o = {x: source.x, y: source.y};
        return diagonal({source: o, target: o});
      })
      .remove();

  // Adding in text for link text here
  // Update the link text
    var linktext = svg.selectAll("g.link")
        .data(links, function (d) {
        return d.target.id;
    });

    linktext.enter()
        .insert("g")
        .attr("class", "link")
        .append("text")
        .attr("dy", ".35em")
        .attr("dy", "-2")
        .attr("text-anchor", "middle")
        .text(function (d) {
        return d.target.linkText;});

    // Transition link text to their new positions
    linktext.transition()
        .duration(duration)
        .attr("transform", function (d) {
          return "translate(" + 
            ((d.source.y + d.target.y) / 2) + "," 
            + ((d.source.x + d.target.x) / 2) + ")";
          //return "translate(" + 500 + "," + 500 + ")";
    })

    //Transition exiting link text to the parent's new position.
    linktext.exit().transition()
      .duration(duration)
      .attr("transform", function(d) {
        return "translate(" + source.y + "," + source.x + ")"; })
        .remove();


  //End of linktext addition


  // Stash the old positions for transition.
  nodes.forEach(function(d) {
    d.x0 = d.x;
    d.y0 = d.y;
  });   

}


// Toggle children.
function toggle(d) {
  if (d.children) {
    d._children = d.children;
    d.children = null;
  } else {
    d.children = d._children;
    d._children = null;
  };       
}




//Update tree with our new data
function updateD3(newData) {
    var changeLink = svg.selectAll("path.link");

    changeLink.transition().duration(2000)
       .attr("stroke-width",function(d) {
        var targName = d.target.name;
        return newData[targName]["linkWidth"];})

    var changeText = svg.selectAll("g.link").select("text");  
    changeText.transition().duration(2000)

        .text(function (d) {
        var targName = d.target.name;
        return newData[targName]["linkText"]
            })
        .attr("dy", ".35em")
        .attr("dy", "-2")
        .attr("text-anchor", "middle")
        .style("fill-opacity", 1);

}

//In reality the jsonResponse would come from an ajax call, but it's formatted in this manner.
//First overwrite our original json data file, then update our chart
$(function(){
     $('#newData').click('submit', function(e){

jsonResponse = {"11p1Raise": {"linkWidth": "9.87", "linkText": "98.7"}, "8p2Bet": {"linkWidth": "9.90", "linkText": "99.0"}, "10p1Call": {"linkWidth": "0.07", "linkText": "0.7"}, "6p1Call": {"linkWidth": "9.90", "linkText": "99.0"}, "14p2Check": {"linkWidth": "0.10", "linkText": "1.0"}, "2p2Fold": {"linkWidth": "0.07", "linkText": "0.7"}, "9p1Fold": {"linkWidth": "0.07", "linkText": "0.7"}, "13p2Call": {"linkWidth": "9.90", "linkText": "99.0"}, "7p1Check": {"linkWidth": "3.14", "linkText":"31.4"}, "12p2Fold": {"linkWidth": "0.10", "linkText": "1.0"}, "1p1Bet": {"linkWidth": "6.86", "linkText": "68.6"}, "0": {"linkWidth": 4, "linkText": ""}, "5p1Fold": {"linkWidth": "0.10", "linkText": "1.0"}, "4p2Raise": {"linkWidth": "9.87", "linkText": "98.7"}, "3p2Call": {"linkWidth": "0.07", "linkText": "0.7"}}

        overwriteD3Json(jsonResponse)
        updateD3(jsonResponse);

            })
     });

//Rewrites our original json used to create the chart with our new information
function overwriteD3Json(newDataObject){
//Just want to loop over tree, rewrite all node text and thickness values
function jsonLoop(tree) {

    var name=tree.name;
    tree["linkText"]=newDataObject[name].linkText
    tree["linkWidth"]=newDataObject[name].linkWidth

  //Run the loop on child nodes, if they exist
  if (tree.hasOwnProperty("children")) {
  for (var i=0; i< tree.children.length; i++){
  jsonLoop(tree.children[i]);
  }
}}

//Run the loop on our main tree
jsonLoop(treeStructure)
}

Collapse a branch by clicking on a parent node, then click the "New Data Update" button in the top left corner. 通过单击父节点折叠分支,然后单击左上角的“新数据更新”按钮。 The data should change, but if you reopen the collapsed branches they have NOT been updated. 数据应该会更改,但是如果您重新打开折叠的分支,则它们尚未更新。 You can confirm this by reclicking the "New Data Update" button and seeing that they should have been updated with these values the first time the button was clicked. 您可以通过重新单击“新数据更新”按钮并在第一次单击按钮时看到应该使用这些值进行更新来确认这一点。

I attempted to solve this by writing over the original json file used to create the tree (this is still in the jsfiddle code), but it didn't work. 我试图通过覆盖用于创建树的原始json文件(仍在jsfiddle代码中)来解决此问题,但是它没有用。 I also tried calling the updateD3 function at various points in the toggle function (not currently in the jsfiddle code), but that didn't work either. 我还尝试在切换功能的各个点(当前不在jsfiddle代码中)调用updateD3函数,但这也不起作用。

My D3 skills are limited so any advice about anything else in the code would be appreciated too! 我的D3技能很有限,因此对代码中其他内容的任何建议也将不胜感激!

The problem is if you click on a node, its children will be either stored as d.children or d._children. 问题是,如果单击某个节点,则其子节点将存储为d.children或d._children。

You are not updating _children property. 您没有更新_children属性。

Add this to your code. 将此添加到您的代码。

if (tree.hasOwnProperty("_children")) {
    for (var i=0; i< tree._children.length; i++){
         jsonLoop(tree._children[i]);
    }
}

Hope this helps. 希望这可以帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM