dash14 / v-network-graph

An interactive network graph visualization component for Vue 3
https://dash14.github.io/v-network-graph/
MIT License
495 stars 44 forks source link

Add/remove operations failed to render #52

Closed happylilem closed 2 years ago

happylilem commented 2 years ago

Hi, I have been trying to reproduce the add/remove nodes and edges operations on my end, but when I click the buttons, no nodes/edges could be added or removed.

I can tell that the buttons are working because after I select a node or an edge, the "remove" buttons would be enabled, which means that the functions should be partially working. So the lines which failed to work should be nodes[nodeId] = { name } for function addNode() and delete nodes[nodeId] for function removeNode().

The only differences between my code and the demo in the documentation is that I'm using a d3 force layout, and that my data is imported from a JSON file with vNG. I added a checkbox to enable/disable d3-force, but even after I disabled it and have the graph present in a simple layout, the add/remove operations still wouldn't work. Is there a possible cause why this might have happened? Thank you in advance!

dash14 commented 2 years ago

Hi @happylilem, Maybe, an object imported from JSON is not reactive, so it is not detected changes. After importing from JSON, try wrapping it with reactive() to make changes to the resulted object.

Sample code is shown below:

<script setup lang="ts">
import { reactive } from "vue";
import testData from "./test-data.json";

const data = reactive(testData);

function addNode() {
  const number = Object.keys(data.nodes).length + 1;
  const nodeId = `node${number}`;
  const name = `Node ${number}`;
  data.nodes[nodeId] = { name };
}
</script>

<template>
  <div class="graph">
    <v-network-graph
      :nodes="data.nodes"
      :edges="data.edges"
      :layouts="data.layouts"
      :configs="data.configs"
    />
    <button @click="addNode">Add a node</button>
  </div>
</template>

<style>
.graph {
  border: 1px solid #888;
  width: 600px;
  height: 400px;
  margin: 0 auto;
}
</style>

test-data.json

{
  "nodes": {
    "node1": { "name": "Node 1" },
    "node2": { "name": "Node 2" },
    "node3": { "name": "Node 3" },
    "node4": { "name": "Node 4" }
  },
  "edges": {
    "edge1": { "source": "node1", "target": "node2" },
    "edge2": { "source": "node2", "target": "node3" },
    "edge3": { "source": "node3", "target": "node4" }
  },
  "layouts": {
    "nodes": {
      "node1": { "x": 0, "y": 0 },
      "node2": { "x": 70, "y": 70 },
      "node3": { "x": 140, "y": 0 },
      "node4": { "x": 210, "y": 70 }
    }
  },
  "configs": {
    "node": {
      "normal": {
        "radius": 20
      }
    }
  }
}
happylilem commented 2 years ago

I did const nodes: Nodes = reactive({ ...data.nodes }) const edges: Edges = reactive({ ...data.edges }) instead of const data = reactive(testData); I guess they function similarly? But I've tried either way and the addNode function still wouldn't work...

My code looks like this:

import * as vNG from "v-network-graph" import { Nodes, Edges } from "v-network-graph" import { reactive, ref, computed } from "vue" import data from "./test-data.json"

const nodes: Nodes = reactive({ ...data.nodes }) const edges: Edges = reactive({ ...data.edges }) const nextNodeIndex = ref(Object.keys(nodes).length + 1) const nextEdgeIndex = ref(Object.keys(edges).length + 1)

const selectedNodes = ref<string[]>([]) const selectedEdges = ref<string[]>([])

function addNode() { const nodeId = node${nextNodeIndex.value} const name = N${nextNodeIndex.value} nodes[nodeId] = { name } nextNodeIndex.value++ }

happylilem commented 2 years ago

Problem solved! It isn't because I didn't set the graph to be reactive, but that inside of <v-network-graph> tag, using

:nodes="data.nodes"
:edges="data.edges"
:configs="data.configs"

prevented the graph from rendering the changes because it's always using nodes from the original data file. So I took away the data. part and did:

:nodes="nodes"
:edges="edges"
:configs="configs"

For graphs with vNG, all features for the added Node are needed to make a new node, like this:

function addNode() {
  const nodeId = nextNodeIndex.value
  nodes[nodeId] = {
    name: String(nodeId),
    size: 16,
    color: "blue",
    label: true
  }
  nextNodeIndex.value++

The only problem left is the same as described in issue 54, a node cannot be removed when it's connected to another node with an edge. I need to delete the edge first and then delete the node.

dash14 commented 2 years ago

Hi @happylilem, Thanks for your report. Glad you have it resolved. Please check the issue when deleting a node (#54), as it has been fixed in v0.5.12.

dash14 commented 2 years ago

I close this issue for now. If you have any other problems, please open a new issue.