dash14 / v-network-graph

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

Unable to delete node with associated edge when d3-force enabled #68

Closed happylilem closed 2 years ago

happylilem commented 2 years ago

Hi, I'm using d3-force layout for my network and found that when a node has any edge connected to it, the remove node function wouldn't work. I did a few trials and can confirm that this issue only occurs when d3-force is enabled. As long as d3-force is disabled, the node and the associated edges can be removed. So I guess this is a bug in the library?

dash14 commented 2 years ago

Hi @happylilem, I tried but could not reproduce it on my end. Is it possible for you to provide the minimum code?

I noticed in the process of checking, is the following code which included in the example using d3-force, also included in your code? This code reconstructs the nodes and edges, so if it is included, this process may be the cause. If not, sorry.

watchEffect(() => {
  buildNetwork(nodeCount.value, nodes, edges)
})
happylilem commented 2 years ago

I don't have that code snippet included in my code, but here's a minimum code that can reproduce the problem--

App.vue

<script setup lang="ts">
import { reactive, ref, computed } from "vue"
import * as vNG from "v-network-graph"
import { Nodes, Edges } from "v-network-graph"
import {
  ForceLayout,
} from "v-network-graph/lib/force-layout"
import data from "./test-data.json"

const nodes: Nodes = reactive({ ...data.nodes })
const edges: Edges = reactive({ ...data.edges })

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

const configs = reactive(
  vNG.defineConfigs({
    view: {
      minZoomLevel: 0.1,
      maxZoomLevel: 16,
      layoutHandler: new ForceLayout({
        positionFixedByDrag: false,
        positionFixedByClickWithAltKey: true,
      }),
    },
    node: {
      selectable: 2,
    },
    edge: {
      selectable: true,
      normal: {
        width: 3,
      },
    },
  })
)

const d3ForceEnabled = computed({
  get: () => configs.view.layoutHandler instanceof ForceLayout,
  set: (value: boolean) => {
    if (value) {
      configs.view.layoutHandler = new ForceLayout()
    } else {
      configs.view.layoutHandler = new vNG.SimpleLayout()
    }
  },
})

function removeNode() {
  for (const nodeId of selectedNodes.value) {
    delete nodes[nodeId]
  }
}

function removeEdge() {
  for (const edgeId of selectedEdges.value) {
    delete edges[edgeId]
  }
}
</script>

<template>
  <div class="demo-control-panel">
    <div>
      <label>Node:</label>
      <el-button :disabled="selectedNodes.length == 0" @click="removeNode"
        >remove</el-button
      >
    </div>
    <div>
      <label>Edge:</label>
      <el-button :disabled="selectedEdges.length == 0" @click="removeEdge"
        >remove</el-button
      >
    </div>
  </div>

  <div>
    <input type="checkbox" id="force" v-model="d3ForceEnabled">
    <label for="force">D3-Force enabled</label>
  </div>

  <v-network-graph
    v-model:selected-nodes="selectedNodes"
    v-model:selected-edges="selectedEdges"
    :nodes="nodes"
    :edges="edges"
    :layouts="data.layouts"
    :configs="configs"
  />
</template>

test-data.json file:

{
  "nodes": {
      "1": {"name": "node 1", "size": 32, "color": "gray", "label": true },
      "2": {"name": "node 2", "size": 24, "color": "pink", "label": true },
      "3": {"name":"node 3", "size": 16, "color": "pink", "label": true },
      "4": {"name":"node 4", "size": 16, "color": "pink", "label": true }
  },
  "edges": {
      "1": { "source": "1", "target": "2", "width": 3},
      "2": { "source": "1", "target": "3", "width": 3 },
      "3": { "source": "1", "target": "4", "width": 3 },
      "4": { "source": "2", "target": "3", "width": 1 }
    }
}

Besides, after I disabled d3-force and deleted a node with its associated edges and then switched back to d3-force positioning, I'm not able to operate the graph anymore since it throws this error: Uncaught (in promise) Error: node not found: 4 (where 4 is the number of node that has been removed).

dash14 commented 2 years ago

Hi @happylilem, Thank you for providing a reproducible snippet! I now understand the situation that is an error.

I've fixed and released. With the new release(v0.6.3), when passing a list of edges to d3-force, edges containing non-existent nodes are excluded. However, note that v-network-graph does not modify the contents of nodes and edges, so edges contains nodes that do not exist actually.

Best Regards

dash14 commented 2 years ago

I close this issue for now. If you have any questions, please open a new issue or re-open this.