bcakmakoglu / vue-flow

A highly customizable Flowchart component for Vue 3. Features seamless zoom & pan πŸ”Ž, additional components like a Minimap πŸ—Ί and utilities to interact with state and graph.

Home Page:https://vueflow.dev/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

πŸ› [BUG]: Node added in `onMounted()` is not rendered

DavidPfl opened this issue Β· comments

Is there an existing issue for this?

  • I have searched the existing issues and this is a new bug.

Current Behavior

I use the example shown in Adding nodes to the graph.
The node that is pushed in the onMounted() function is not rendered.
When I use Vue dev tools, I see that the event nodesInitialized event does not contain the node that is pushed to the elements array in onMounted(), but only the node that is part of the initial elements definition.

Here is the code I'm using:

<script setup>
import { ref, onMounted } from 'vue'
import { VueFlow } from '@vue-flow/core'

const elements = ref([
  {
    id: '1',
    position: { x: 50, y: 50 },
    label: 'Node 1',
  }
]);

onMounted(() => {
  elements.value.push({
    id: '2',
    label: 'Node 2',
    position: { x: 150, y: 50 },
  })
})
</script>

<template>
  <VueFlow v-model="elements"/>
</template>

Expected Behavior

I expect the node that is pushed to the elements array to be rendered when I refresh the page.

Steps To Reproduce

  1. Use the code from the first example in Adding nodes to the graph
  2. npm run dev in your App folder

Relevant log output

No response

Anything else?

As per my package-lock.json, I am using "@vue-flow/core": "^1.33.4".

Thanks for the report.
Alas, because of the way the v-models (elements, nodes and edges) are setup at the moment I expect that this issue will persist for the remainder of VueFlow 1.

I will address this problem with the next major update - for now you can use onPaneReady if you need to add some nodes after the initial ones, though be aware that fit-view-on-init will not play nicely with this and won't consider nodes that have been added after that. You can always call fitView yourself though, so that's not a big problem :)

A little bit of background on why this happens:
Currently when elements are passed to VueFlow they are "transformed" from Node -> GraphNode / Edge -> GraphEdge, adding some additional pieces of info to these elements that are necessary for VueFlow to work correctly.

To allow for direct mutation of these objects like node.position.x = 100 without having to pass a new array of elements, the transformed elements are pushed into your model ref.
So basically what happens is this

  1. You pass VueFlow your elements ref
  2. The elements get transformed
  3. Transformed elements overwrite your elements ref value
    To avoid having infinite loops happening while this is done, a pausable watcher is used that waits until your elements ref is overwritten to continue watching for changes.
    When you do elements.value.push(...) during onMounted it ends up happening during the pause of this watcher so the new element is never noticed and consumed by VueFlow :/

What does work is onMounted(() => setTimeout(() => {}, 0)) but obviously this isn't a great pattern to use.

In the next major version I'll see that this binding is solved differently since the current implementation has been giving me headaches for too long but re-doing it would be a breaking change since the whole "transformation" needs to stop and be replaced with an internal map that contains these additional pieces of information that VueFlow needs.

Anyway - sorry that I can't give you a better solution at this point in time.
I'll keep this issue open though.

@bcakmakoglu Thanks a lot for the detailed explanation and the workarounds.