D3图不会显示动态创建的JSON

时间:2018-12-11 19:50:49

标签: javascript node.js json d3.js ejs

我有一个函数,该函数会在页面加载getVisualizer()来创建当前用户及其朋友的JSON文件。这里的目标是可视化用户网络。因此,最终结果应该是当前用户节点,该节点附加到标有其朋友名称的节点上。

此处设置D3来渲染称为“链接”的图形。我有一个可以渲染的示例图(标题为previous_links),但是当我尝试创建自己的名为“ links”的JSON文件时,该图不再绘制。我不知所措,因为我检查了是否将正确的数据添加到JSON文件对象中,因此我认为这可能是格式问题。

相关代码如下。

 <!doctype html>
    <html lang="en">
      <head>
        <style>....

        </style>
      </head>

    <body>     
      <div id = "trythis" class="container main-section">
      //...basic layout items here...//


    <script>

    var numRequests = 0;

    var links = [];

    //PROBLEM SOMEWHERE IN HERE.--------

    function getVisualizer() {
        $.getJSON('/visualizer', function(data) {
          var n = Object.keys(data).length;
          if (n !== numRequests) {
            for (var key in data) {
              var value = data[key];
              var myObj = {
             "source" : "MyName",    //Test Username variable
             "target" : value       //friends name variable
            };
            //push the object to JSON array
            console.log("The object added is" + JSON.stringify(myObj));
            links.push( myObj );
            }
            numRequests = n;
          }
          console.log("Reached end of function");
        });
      };

      function refresh() {
        getVisualizer();

        //test that item 1 is correct
        console.log(JSON.stringify(links[0]) + "is links 0");
      };


        refresh();


    //PARTIAL ISSUE HERE. THIS GRAPH DRAWS SUCCESSFULLY WHEN RENAMED "links"
    //As you can see, I tried to create the same data structure 
    //above when loading friends and calling "push" for each object.

    //---------SAMPLE GRAPH THAT DRAWS--------------------

    var previous_links = [{
        "source": "Analytics",
        "target": "Science"
    }, {
        "source": "Analytics",
        "target": "Software"
    }, {
        "source": "Analytics",
        "target": "Story"
    }, {
        "source": "Science",
        "target": "Math"
    }, {
        "source": "Science",
        "target": "Statistics"
    }, {
        "source": "Software",
        "target": "R"
    }, {
        "source": "Software",
        "target": "SAS"
    }, {
        "source": "Software",
        "target": "Other"
    }, {
        "source": "Story",
        "target": "Business Communication"
    }, {
        "source": "Story",
        "target": "Visualization"
    }];

    //----------------------------------  

    //Here, I set up the graph for JSON named "links". Question is, how come
    //my newly created version will not draw?

    var nodes = {}

    // Compute the distinct nodes from the links.
    links.forEach(function (link) {
        link.source = nodes[link.source] || (nodes[link.source] = {
            name: link.source
        });
        link.target = nodes[link.target] || (nodes[link.target] = {
            name: link.target
        });
        link.value = +link.value;
    });

    var width = 900
    height = 300;

    var force = d3.layout.force()
        .nodes(d3.values(nodes))
        .links(links)
        .size([width, height])
        .linkDistance(50)
        .charge(-200)
        .on("tick", tick)
        .start();

    var svg = d3.select("#trythis").append("svg")
        .attr("width", width)
        .attr("height", height);

    var link = svg.selectAll(".link")
        .data(force.links())
        .enter().append("line")
        .attr("class", "link");

    var node = svg.selectAll(".node")
        .data(force.nodes())
        .enter().append("g")
        .attr("class", "node")
        .on("mouseover", mouseover)
        .on("mouseout", mouseout)
        .on("click", click)
        .on("dblclick", dblclick)
        .call(force.drag);

    node.append("circle")
        .attr("r", 12)
        .style("fill", "#C71585");

    node.append("text")
        .attr("x", 14)
        .attr("dy", ".35em")
        .style("fill", "#333")
        .text(function (d) {
        return d.name;
    });

    function tick() {
        link.attr("x1", function (d) {
            return d.source.x;
        })
            .attr("y1", function (d) {
            return d.source.y;
        })
            .attr("x2", function (d) {
            return d.target.x;
        })
            .attr("y2", function (d) {
            return d.target.y;
        });

        node.attr("transform", function (d) {
            return "translate(" + d.x + "," + d.y + ")";
        });
    }

    function mouseover() {
        d3.select(this).select("circle").transition()
            .duration(750)
            .attr("r", 16);
    }

    function mouseout() {
        d3.select(this).select("circle").transition()
            .duration(750)
            .attr("r", 12);
    }
    // action to take on mouse click
    function click() {
        d3.select(this).select("text").transition()
            .duration(750)
            .attr("x", 22)
            .style("stroke-width", ".5px")
            .style("opacity", 1)
            .style("fill", "#E34A33")
            .style("font", "17.5px serif");
        d3.select(this).select("circle").transition()
            .duration(750)
            .style("fill", "#E34A33")
            .attr("r", 16)
    }

    // action to take on mouse double click
    function dblclick() {
        d3.select(this).select("circle").transition()
            .duration(750)
            .attr("r", 12)
            .style("fill", "#E34A33");
        d3.select(this).select("text").transition()
            .duration(750)
            .attr("x", 14)
            .style("stroke", "none")
            .style("fill", "#E34A33")
            .style("stroke", "none")
            .style("opacity", 0.6)
            .style("font", "14px serif");
    }





    </script>
    </div>
    </div>
    </div>
    </div>
    </div>

    </body>
    </html>

1 个答案:

答案 0 :(得分:0)

我很难在没有示例项目运行的情况下进行验证,但这是我认为已发生的事情:

links.forEach(function (link) {运行时,links仍具有值[],因为function中的$.getJSON('/visualizer', function(data) {异步运行,并且很可能在运行后发生您其余的代码。

尝试模块化您的初始化代码,并在异步代码之后调用它。像这样:

<script>
  let numRequests = 0
  let links = []
  let nodes = {}
  let link
  let node

  let testData = [{
    "source": "Analytics",
    "target": "Science"
  }, {
    "source": "Analytics",
    "target": "Software"
  }, {
    "source": "Analytics",
    "target": "Story"
  }, {
    "source": "Science",
    "target": "Math"
  }, {
    "source": "Science",
    "target": "Statistics"
  }, {
    "source": "Software",
    "target": "R"
  }, {
    "source": "Software",
    "target": "SAS"
  }, {
    "source": "Software",
    "target": "Other"
  }, {
    "source": "Story",
    "target": "Business Communication"
  }, {
    "source": "Story",
    "target": "Visualization"
  }]


  const addIfUnique = (list, item) => list.indexOf(item) === -1 ? list.concat(item) : list
  const byName = (links, {source, target}) => addIfUnique(addIfUnique(links, source), target)
  const toNode = (name, i) => ({name, index: i})

  function addLinks(data) {
    let names = data.reduce(byName, [])
    const toLink = ({source, target}) => ({
      source: names.indexOf(source),
      target: names.indexOf(target)
    })
    nodes = names.map(toNode)
    links = data.map(toLink)
  }

  function tick() {
    link.attr("x1", d => d.source.x)
      .attr("y1", d => d.source.y)
      .attr("x2", d => d.target.x)
      .attr("y2", d => d.target.y)

    node.attr("transform", d => `translate(${d.x},${d.y})`)
  }

  function mouseover() {
    d3.select(this).select("circle").transition()
      .duration(750)
      .attr("r", 16)
  }

  function mouseout() {
    d3.select(this).select("circle").transition()
      .duration(750)
      .attr("r", 12)
  }

  function click() {
    d3.select(this).select("text").transition()
      .duration(750)
      .attr("x", 22)
      .style("stroke-width", ".5px")
      .style("opacity", 1)
      .style("fill", "#E34A33")
      .style("font", "17.5px serif")

    d3.select(this).select("circle").transition()
      .duration(750)
      .style("fill", "#E34A33")
      .attr("r", 16)
  }

  function dblclick() {
    d3.select(this).select("circle").transition()
      .duration(750)
      .attr("r", 12)
      .style("fill", "#E34A33")

    d3.select(this).select("text").transition()
      .duration(750)
      .attr("x", 14)
      .style("stroke", "none")
      .style("fill", "#E34A33")
      .style("stroke", "none")
      .style("opacity", 0.6)
      .style("font", "14px serif")
  }

  function drawGraph() {
    const width = 900
    const height = 300

    const force = d3.layout.force()
      .nodes(d3.values(nodes))
      .links(links)
      .size([width, height])
      .linkDistance(50)
      .charge(-200)
      .on("tick", tick)
      .start()

    const svg = d3.select("#trythis").append("svg")
      .attr("width", width)
      .attr("height", height)

    link = svg.selectAll(".link")
      .data(force.links())
      .enter().append("line")
      .attr("class", "link")

    node = svg.selectAll(".node")
      .data(force.nodes())
      .enter().append("g")
      .attr("class", "node")
      .on("mouseover", mouseover)
      .on("mouseout", mouseout)
      .on("click", click)
      .on("dblclick", dblclick)
      .call(force.drag)

    node.append("circle")
      .attr("r", 12)
      .style("fill", "#C71585")

    node.append("text")
      .attr("x", 14)
      .attr("dy", ".35em")
      .style("fill", "#333")
      .text(d => d.name)
  }

  function verifyItem1IsCorrect() {
    const item1 = JSON.stringify(links[0])
    console.log(`${item1} is links 0`)
  }

  function setUpGraph(data) {
    addLinks(data)
    drawGraph()
    verifyItem1IsCorrect()
  }

  // $.getJSON('/visualizer', setUpGraph)
  setUpGraph(testData)
</script>

编辑:D3.js v3 API具有linksnodes所需的非常特定的布局。

nodes数组中的对象带有一个简单的整数索引和一个字符串名称,links数组中的对象具有源和目标属性,但源和目标必须是:

  • 整数,指节点的索引;或
  • 对象引用了节点数组中的实际节点。