visjs / vis-network

:dizzy: Display dynamic, automatically organised, customizable network views.
https://visjs.github.io/vis-network/
Apache License 2.0
2.99k stars 365 forks source link

Nodes Hide All Instead of Only Children in vis-network #2164

Closed egorgoncharenko174 closed 3 months ago

egorgoncharenko174 commented 3 months ago

Description: In a Vue.js application using vis-network, attempting to hide only the immediate children of a node on double-click results in all nodes being hidden instead of just the intended children.

Steps to Reproduce:

  1. Initialize a network with hierarchical layout using vis-network.
  2. Load nodes and edges dynamically from an API.
  3. Implement double-click event to hide only the children nodes of the clicked node.

Expected Behavior: Only the immediate children of the clicked node should be hidden.

Actual Behavior: All nodes in the network are hidden.

Code Example:

<template>
  <div id="mynetwork" />
</template>

<script>
import { Network } from 'vis-network'
import { DataSet } from 'vis-data'
import {
  getSystems,
  getDigitalProcesses,
} from '@/api/digital-processes'

export default {
  data() {
    return {
      nodesArray: [],
      edgesArray: [],
      network: null,
      hiddenNodes: new Set(),
    }
  },
  async mounted() {
    await this.fetchData()
    this.initializeNetwork()
  },
  methods: {
    async fetchData() {
      await this.fetchSystems()
    },
    async fetchSystems() {
      try {
        const response = await getSystems()
        const systems = response.data.data

        for (const system of systems) {
          this.nodesArray.push({
            id: system.id,
            title: system.attributes.name,
            label: system.attributes.description,
            type: system.type,
            quantityOfChildren: system.attributes.quantityOfChildren,
            quantityOfInputRelationships: system.attributes.quantityOfInputRelationships,
            quantityOfOutputRelationships: system.attributes.quantityOfOutputRelationships,
            widthConstraint: { minimum: 120 },
          })
          await this.fetchDigitalProcesses(system.id)
        }
      } catch (e) {
        console.error('Ошибка при получении данных систем:', e)
      }
    },
    async fetchDigitalProcesses(systemId) {
      try {
        const dpResponse = await getDigitalProcesses(systemId)
        const digitalProcesses = dpResponse.data.data
        for (const dp of digitalProcesses) {
          this.nodesArray.push({
            id: dp.id,
            title: dp.attributes.name,
            label: dp.attributes.description,
            type: dp.type,
            quantityOfChildren: dp.attributes.quantityOfChildren,
            quantityOfInputRelationships: dp.attributes.quantityOfInputRelationships,
            quantityOfOutputRelationships: dp.attributes.quantityOfOutputRelationships,
            widthConstraint: { minimum: 120 },
          })
          this.edgesArray.push({
            from: dp.attributes.parent.id,
            to: dp.id,
            smooth: { enabled: true, type: 'vertical' },
          })
        }
      } catch (e) {
        console.error('Ошибка при получении цифровых процессов:', e)
      }
    },
    loadSavedNodes() {
      let savedNodes = localStorage.getItem('savedNodes')
      if (savedNodes) {
        savedNodes = JSON.parse(savedNodes)
        this.nodesArray.forEach(node => {
          const savedNode = savedNodes.find(n => n.id === node.id)
          if (savedNode) {
            node.x = savedNode.x
            node.y = savedNode.y
          }
        })
      }
    },
    initializeNetwork() {
      const nodes = new DataSet(this.nodesArray)
      const edges = new DataSet(this.edgesArray)

      this.nodes = nodes
      this.edges = edges

      const categoryColors = {
      }

      this.nodesArray.forEach(node => {
        const color = categoryColors[node.type] || '#D3D3D3'
        node.color = { background: color }
        node.label = `<b>${node.title}</b>\n${node.label}`
      })

      const container = this.$el
      const data = { nodes, edges }
      const options = {
        layout: {
          hierarchical: {
            direction: 'UD',
            sortMethod: 'directed',
            nodeSpacing: 200,
          },
        },
        edges: {
          font: { size: 12 },
          widthConstraint: { maximum: 90 },
        },
        nodes: {
          shape: 'box',
          margin: 10,
          widthConstraint: { maximum: 200 },
          font: { multi: 'html' },
        },
        physics: { enabled: false },
      }

      this.network = new Network(container, data, options)

      this.loadSavedNodes()
      setTimeout(() => {
        this.network.setOptions({
          layout: { hierarchical: false },
        })
      }, 3000)

      this.network.on('dragEnd', this.saveNodePositions)
      this.network.on('doubleClick', this.handleDoubleClick)
    },
    saveNodePositions() {
      const positions = this.network.getPositions()
      const nodes = this.nodesArray.map(node => {
        const position = positions[node.id]
        if (position) {
          return { id: node.id, x: position.x, y: position.y }
        }
        return { id: node.id, x: node.x, y: node.y }
      })
      localStorage.setItem('savedNodes', JSON.stringify(nodes))
    },
    handleDoubleClick(params) {
      if (params.nodes.length > 0) {
        const nodeId = params.nodes[0]
        const children = this.edgesArray
          .filter(edge => edge.from === nodeId)
          .map(edge => edge.to)

        console.log(`Node ID: ${nodeId}`)
        console.log(`Children IDs: ${children.join(', ')}`)

        children.forEach(childId => {
          this.hiddenNodes.add(childId)
          const node = this.network.body.nodes[childId]
          if (node) {
            node.setOptions({ hidden: true })
            console.log(`Hiding node with ID: ${childId}`)
          }
        })

        const edgesToShow = this.edgesArray.filter(edge =>
          !this.hiddenNodes.has(edge.from) && !this.hiddenNodes.has(edge.to))
        this.edges.update(edgesToShow)

        console.log('Updated hidden nodes:', Array.from(this.hiddenNodes))
      }
    },
  },
}
</script>

<style scoped>
#mynetwork {
  width: 100%;
  height: 800px;
  border: 1px solid lightgray;
}
</style>

Additional Information: vis-data version: "^7.1.9", vis-network version: "^9.1.9", Vue.js version: "^2.7.8"

Logs:

Node ID: 018f9b57-0dc1-77e7-8485-3cafbe97c824
Children IDs: 018f9b57-0de4-761e-ac8b-f3054ce9fb33, 018f9b57-0de4-7acf-8ee5-290dbf889b0b, 018f9b57-0de4-7ee4-901b-ce6d8da39a36, 018f9b57-0de4-7fc6-b932-9a4b0a44e74c
Hiding node with ID: 018f9b57-0de4-761e-ac8b-f3054ce9fb33
Hiding node with ID: 018f9b57-0de4-7acf-8ee5-290dbf889b0b
Hiding node with ID: 018f9b57-0de4-7ee4-901b-ce6d8da39a36
Hiding node with ID: 018f9b57-0de4-7fc6-b932-9a4b0a44e74c
Updated hidden nodes: ['018f9b57-0de4-761e-ac8b-f3054ce9fb33', '018f9b57-0de4-7acf-8ee5-290dbf889b0b', '018f9b57-0de4-7ee4-901b-ce6d8da39a36', '018f9b57-0de4-7fc6-b932-9a4b0a44e74c']