Home > Enterprise >  Vue.js and D3.js - force-simualtion
Vue.js and D3.js - force-simualtion

Time:12-12

I'm currently trying to consume an Restful-Api call and use the infromation, to make an d3.js force-simulation. The problem is that if I use the data from an API, call the simulation handles it if there was nothing. If I wait for the next tick this.$nextTick(simu) all positions end up as an NaN. Is there a reason for this behaviour?

const URL = 'https://jsonplaceholder.typicode.com/posts';

new Vue({
  el: '#app',
  data() {
    return {
      webGraph: {
        nodes: [],
        edges: []
      },
      graph1: {
        nodes:[
          {url:2},
          {url:3},
        ],
        edges:[
          {source:2, target:3},
        ]
      }
    }
  },
  created() {
    axios.get(URL).then((response) => {
      let node1 = {
        url: response.data[1].id
      }
      let node2 = {
        url: response.data[2].id
      }
      let edge = {
        source: {url:response.data[1].id},
        target: {url:response.data[2].id}
      }
      this.webGraph.nodes.push(node1)
      this.webGraph.nodes.push(node2)
      this.webGraph.edges.push(edge)
    })
    
  d3.forceSimulation(this.webGraph.nodes)
      .force("charge", d3.forceManyBody().strength(-25))
      .force("link", d3.forceLink().id(d => d.url).links(this.webGraph.edges))
      .on('end', function() {
        console.log("done")
      });
  d3.forceSimulation(this.graph1.nodes)
      .force("charge", d3.forceManyBody().strength(-25))
      .force("link", d3.forceLink().id(d => d.url).links(this.graph1.edges))
      .on('end', function() {
        console.log("done")
      });
  

  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
  <h6>{{webGraph}}</h6>
  <br> 
  <h6>{{graph1}}</h6>
</div>

CodePudding user response:

The reason why webGraph and graphData1 have different outcomes, is that the simulation of webGraph is started before it has data. If you move the simulation code inside inside the axios.get().then, then you'll see it works as you'd expect.

const URL = 'https://jsonplaceholder.typicode.com/posts';

new Vue({
  el: '#app',
  data() {
    return {
      webGraph: {
        nodes: [],
        edges: []
      },
      graph1: {
        nodes:[
          {url:2},
          {url:3},
        ],
        edges:[
          {source:2, target:3},
        ]
      }
    }
  },
  created() {
    axios.get(URL).then((response) => {
      let node1 = {
        url: response.data[1].id
      }
      let node2 = {
        url: response.data[2].id
      }
      let edge = {
        source: node1,
        target: node2
      }
      
      this.webGraph = {
        nodes: [node1, node2],
        edges: [edge]
      };
      
      d3.forceSimulation(this.webGraph.nodes)
        .force("charge", d3.forceManyBody().strength(-25))
        .force("link", d3.forceLink().id(d => d.url).links(this.webGraph.edges))
        .on('end', function() {
          console.log("done")
        });
    })

  d3.forceSimulation(this.graph1.nodes)
      .force("charge", d3.forceManyBody().strength(-25))
      .force("link", d3.forceLink().id(d => d.url).links(this.graph1.edges))
      .on('end', function() {
        console.log("done")
      });
  

  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
  <h6>{{webGraph}}</h6>
  <br> 
  <h6>{{graph1}}</h6>
</div>

In your code, I also changed the initialization of edge. It showed that you need to look at the difference between variables and references.

With let edge = { source: {url:response.data[1].id}, target: {url:response.data[2].id} }, if node1 changes, edge.source does not. So edge.source will not know where to point to. With let edge = { source: node1, target: node2 }, the code is not only shorter, but if node1 changes, edge.source is always up to date.

  • Related