Обновление данных макета дерева d3 v5 воссоздает дерево вместо обновления узлов - PullRequest
0 голосов
/ 09 февраля 2020

Я работаю над приложением, в котором я хочу показать свои иерархические данные в древовидной структуре. Эти данные продолжают обновляться, и я хочу обновить дерево согласно вновь полученным данным. Я успешно реализовал его с помощью D3 V3, объединив новые данные с новыми данными.

Теперь я хочу обновить систему до d3 V5. До сих пор мне удалось создать дерево, но я не могу обработать обновление данных. Всякий раз, когда я получаю новые данные, все дерево воссоздается, я вижу, что события enter () и exit () запускаются каждый раз для каждого узла. Примечание. Пожалуйста, не обращайте внимания, если не работает метод развернутого коллапса, я все еще работаю над этим.

Я извлек код, созданный под рабочим фрагментом, чтобы показать мою проблему. Я не хочу воссоздавать дерево каждый раз, когда получаю новые данные, я только хочу обновить существующие узлы, если есть какие-либо изменения в данных, в противном случае узлы остаются как есть.

Где я иду не так? Можете ли вы предложить, пожалуйста?

// Code goes here

// find elements
var margin = {
    top: 20,
    right: 90,
    bottom: 30,
    left: 90
  },
  width = 660 - margin.left - margin.right,
  height = 300 - margin.top - margin.bottom;
i = 0,
  duration = 750,
  redius = 10;


var node = {};
var root = {};
var links = {};
var nodes = [];
var links = [];

// handle click and add class
$("#btnLoadData").click(function() {
  refreshData();
});

function refreshData() {
  var data = {
    "name": "Central",
    "connectionState": Math.random() > 0.5 ? "connected" : "disconnected",
    "parent": null,
    "envStatus": {
      "cpu": 45.575,
      "mem": 55.8,
      "disk": 85.5
    },
    "subState": "",
    "children": [{
      "name": "UK-STORE1",
      "connectionState": Math.random() > 0.5 ? "connected" : "disconnected",
      "subState": "",
      "envStatus": {
        "cpu": 45.650000000000006,
        "mem": 55.8,
        "disk": 85.5
      },
      "children": [{
          "name": "UK-TILL1",
          "connectionState": Math.random() > 0.5 ? "connected" : "disconnected",
          "subState": null,
          "envStatus": {
            "cpu": 46.025000000000006,
            "mem": 55.8,
            "disk": 85.5,

          },
          "children": null,
          "stateChangedAt": "2020-02-08 20:59:35.226769"
        },
        {
          "name": "UK-TILL2",
          "connectionState": Math.random() > 0.5 ? "connected" : "disconnected",
          "subState": null,
          "envStatus": {
            "cpu": 45.775000000000006,
            "mem": 56.1,
            "disk": 85.5
          },
          "children": null,
          "stateChangedAt": "2020-02-08 20:59:35.226769"
        }
      ],
      "stateChangedAt": "2020-02-08 20:59:35.226769"
    }]

  }
  buildRoot(data);
}

function buildRoot(newSource) {
  root = d3.hierarchy(newSource, function(d) {
    return d.children;
  });

  root.x = 0;
  root.y = width / 2;
  root.x0 = 0;
  root.y0 = width / 2;
  updateTree(root)
}

var svg = d3.select("#tree").append("svg")
  .attr("width", width + margin.left + margin.right)
  .attr("height", height + margin.top + margin.bottom);

var mainG = svg.append("g")
  .attr("transform",
    "translate(" + margin.left + "," + margin.top + ")");

var treeLayout = d3.tree()
  .size([width, height]);
//.nodeSize([100, 50]);

// Collapse the node and all it's children
function collapse(d) {
  if (d.children) {
    d._children = d.children
    d._children.forEach(collapse)
    d.children = null
  }
}


function updateTree(source) {
  var treeData = treeLayout(root);
  var newNodes = treeData.descendants();
  _.merge(nodes, newNodes)

  var newlinks = treeData.descendants().slice(1);
  _.merge(links, newlinks)

  nodes.forEach(function(d) {
    dy = d.depth * 180
  });

  //links
  var linkPaths = mainG.selectAll(".link")
    .data(links, function(d) {
      return d.id;
    });

  var linkEnter = linkPaths.enter().append("path")
    .attr("class", "link")
    .attr('d', function(d) {
      var o = {
        x: source.x0,
        y: source.y0
      }
      return diagonal(o, o);
    });
  var linkUpdate = linkEnter.merge(linkPaths);
  linkUpdate.transition()
    .duration(duration)
    .attr('d', function(d) {
      return diagonal(d, d.parent)
    });

  var linkExit = linkPaths.exit()
    .transition()
    .duration(duration)
    .attr('d', function(d) {
      var o = {
        y: source.y0,
        x: source.x0
      }
      return diagonal(o, o)
    })
    .remove();

  //Nodes
  var node = mainG.selectAll('g.node')
    .data(nodes, function(d) {
      return d.id || (d.id = ++i);
    });

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


  //Append circle
  nodeEnter.append("circle")
    .attr("class", "circle")
    .attr("r", redius);

  //Append circle
  nodeEnter.append("text")
    .attr("class", "nodeName")
    .text(function(d) {
      return d.data.name;
    });

  // UPDATE
  var nodeUpdate = nodeEnter.merge(node);

  // Transition to the proper position for the node
  nodeUpdate.transition()
    .duration(duration)
    .attr("transform", function(d) {
      return "translate(" + d.x + "," + d.y + ")";
    });
  nodeUpdate.select("circle")
    .attr("class", function(d) {
      console.log(d.data.connectionState);
      return d.data.connectionState == "connected" ? "circle-connected" : "circle-disconnected"
    })
  // Remove any exiting nodes
  var nodeExit = node.exit().transition()
    .duration(duration)
    .attr("transform", function(d) {
      return "translate(" + source.y + "," + source.x + ")";
    })
    .remove();

  // On exit reduce the node circles size to 0
  nodeExit.select('circle')
    .attr('r', 1e-6);

  // On exit reduce the opacity of text labels
  nodeExit.select('text')
    .style('fill-opacity', 1e-6);

  function diagonal(s, d) {
    path = `M ${s.x} ${s.y}
        C ${s.x} ${(d.y+ s.y)/2}, 
        ${s.x} ${(d.y+ s.y)/2 },
        ${d.x} ${d.y}`
    return path;
  }

  function click(d) {
    if (d.children) {
      d._children = d.children;
      d.children = null;
    } else {
      d.children = d._children;
      d._children = null;
    }
    updateTree(d);
  }


} // update tree ends

refreshData()
/* Styles go here */

body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
  color: #fff;
}

button {
  background: #0084ff;
  border: none;
  border-radius: 5px;
  padding: 8px 14px;
  font-size: 15px;
  color: #fff;
}

svg {
  background-color: forestgreen;
}

.node {
  cursor: pointer;
}

.circle {
  fill: #fff;
  transition: fill 2s;
  r: 10;
}

.circle-connected {
  fill: #14A76C;
  transition: fill 2s;
}

.circle-disconnected {
  fill: #a72314;
  transition: fill 2s;
}

.link {
  fill: none;
  stroke: #ccc;
}
<!DOCTYPE html>
<html>

<head>
  <script data-require="lodash.js@4.17.4" data-semver="4.17.4" src="https://cdn.jsdelivr.net/npm/lodash@4.17.4/lodash.min.js"></script>
  <link rel="stylesheet" href="style.css" />
  <script src="https://cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js"></script>
  <script src="https://d3js.org/d3.v5.min.js"></script>
</head>

<body>
  <div id="banner-message">
    <p>D3 V5 Tree</p>
    <button id="btnLoadData">Update data</button>
  </div>
  <div id="tree"></div>
  <script src="script.js"></script>
</body>

</html>

.

...