π [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
- Use the code from the first example in Adding nodes to the graph
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
- You pass VueFlow your elements ref
- The elements get transformed
- 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 doelements.value.push(...)
duringonMounted
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.