docs(guide): update node page

This commit is contained in:
braks
2023-11-05 18:43:33 +01:00
committed by Braks
parent d0161fd417
commit 162885199f
2 changed files with 537 additions and 252 deletions
+534 -247
View File
@@ -1,9 +1,19 @@
<script setup>
import { VueFlow } from '@vue-flow/core';
import LogosJavascript from '~icons/logos/javascript';
import LogosTypescript from '~icons/logos/typescript-icon';
import { VueFlow, Panel } from '@vue-flow/core';
import { Background } from '@vue-flow/background';
import Check from '~icons/mdi/check';
import Close from '~icons/mdi/close';
import { ref } from 'vue';
import { ref, h } from 'vue';
const ScrollableNode = () => h('div', { class: 'custom-node-container' }, [
h('ul', { class: 'nowheel' }, Array.from({ length: 100 }, (_, i) => h('li', { key: i }, `Item ${i}`)))
]);
const InputFieldNode = () => h('div', { class: 'custom-node-container' }, [
h('input', { class: 'nodrag', placeholder: 'Type something...' })
]);
const defaultNode = ref([
{
@@ -32,66 +42,96 @@ const outputNode = ref([
]);
</script>
# Nodes
# Introduction to Nodes
Nodes are the building blocks of your graph. They represent any sort of data you want to present in your graph.
Nodes are the underlying components of your graph.
They can be any kind of data you want to visualize in your graph, existing independently and being interconnected
through edges to create a data map.
They can exist on their own but can be connected to each other with edges to create a map.
Remember, every node is unique and thus **requires a unique id** and **an [XY-position](/typedocs/interfaces/XYPosition)**.
Each node <span class="font-bold text-blue-500">requires a unique id and
an [xy-position](/typedocs/interfaces/XYPosition).</span>
For the full list of options available for a node, check out the [Node Interface](/typedocs/interfaces/Node).
You can view the full options-list for a node [here](/typedocs/interfaces/Node).
## Adding Nodes to the Graph
## Usage
Nodes are generally created by adding them to the `mode-value` (using `v-model`) or to the `nodes` prop of the Vue Flow component.
This can be done dynamically at any point in your component's lifecycle.
Generally you create nodes by adding them to the model-value or the nodes prop of the Vue Flow component.
:::code-group
```vue
<script>
```vue [<LogosJavascript />]
<script setup>
import { ref, onMounted } from 'vue'
import { VueFlow } from '@vue-flow/core'
export default defineComponent({
components: { VueFlow },
data() {
return {
elements: [
{
id: '1',
position: { x: 50, y: 50 },
label: 'Node 1',
}
]
}
},
mounted() {
// Add an element after mount
this.elements.push(
{
id: '2',
position: { x: 150, y: 50 },
label: 'Node 2',
}
)
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>
<div style="height: 300px">
<VueFlow v-model="elements" />
</div>
<VueFlow v-model="elements"/>
</template>
```
For more advanced graphs that require more state access you will want to use the useVueFlow composable.
[useVueFlow](/typedocs/functions/useVueFlow) will provide the [`addNodes`](/typedocs/interfaces/Actions#addnodes) action,
which you can use to add nodes directly to the state.
```vue [<LogosTypescript />]
This action can be even be used outside the component that is rendering the graph, like a Sidebar or a Toolbar.
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import type { Elements } from '@vue-flow/core'
import { VueFlow } from '@vue-flow/core'
const elements = ref<Elements>([
{
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>
```
:::
If you are working with more complex graphs that necessitate extensive state access, the `useVueFlow` composable should
be employed.
The [`addNodes`](/typedocs/interfaces/Actions#addnodes) action is available
through [useVueFlow](/typedocs/functions/useVueFlow), allowing you to add nodes straight to the state.
What's more, this action isn't limited to the component rendering the graph; it can be utilized elsewhere, like in a
Sidebar or Toolbar.
::: code-group
```vue [<LogosJavascript />]
```vue
<script setup>
import { ref } from 'vue'
import { VueFlow, useVueFlow } from '@vue-flow/core'
const initialNodes = ref([
@@ -101,117 +141,295 @@ const initialNodes = ref([
label: 'Node 1',
}
])
const { addNodes } = useVueFlow({
nodes: initialNodes,
})
const { addNodes } = useVueFlow()
onMounted(() => {
// Add an element after mount
addNodes([
{
id: '2',
position: { x: 150, y: 50 },
label: 'Node 2',
}
])
})
function generateRandomNode() {
return {
id: Math.random().toString(),
position: { x: Math.random() * 500, y: Math.random() * 500 },
label: 'Random Node',
}
}
function onAddNode() {
// add a single node to the graph
addNodes(generateRandomNode())
}
function onAddNodes() {
// add multiple nodes to the graph
addNodes(Array.from({ length: 10 }, generateRandomNode))
}
</script>
<template>
<div style="height: 300px">
<VueFlow />
</div>
<VueFlow :nodes="initialNodes" />
<button type="button" @click="onAddNode">Add a node</button>
<button type="button" @click="onAddNodes">Add multiple nodes</button>
</template>
```
## [Default Node-Types](/typedocs/types/DefaultNodeTypes)
```vue [<LogosTypescript />]
<script setup lang="ts">
import { ref } from 'vue'
import type { Node } from '@vue-flow/core'
import { VueFlow, useVueFlow } from '@vue-flow/core'
Vue Flow comes with built-in nodes that you can use right out of the box.
These node types include `default`, `input` and `output`.
const initialNodes = ref<Node[]>([
{
id: '1',
position: { x: 50, y: 50 },
label: 'Node 1',
data: {},
}
])
const { addNodes } = useVueFlow()
function generateRandomNode() {
return {
id: Math.random().toString(),
position: { x: Math.random() * 500, y: Math.random() * 500 },
label: 'Random Node',
data: {
hello: 'world',
}
}
}
function onAddNode() {
// add a single node to the graph
addNodes(generateRandomNode())
}
function onAddNodes() {
// add multiple nodes to the graph
addNodes(Array.from({ length: 10 }, generateRandomNode))
}
</script>
<template>
<VueFlow :nodes="initialNodes" />
<button type="button" @click="onAddNode()">Add a node</button>
<button type="button" @click="onAddNodes()">Add multiple nodes</button>
</template>
```
:::
## [Predefined Node-Types](/typedocs/types/DefaultNodeTypes)
Vue Flow provides several built-in node types that you can leverage immediately.
The included node types are `default`, `input`, and `output`.
### Default Node
<div class="mt-4 bg-[var(--vp-code-block-bg)] rounded h-50">
<VueFlow v-model="defaultNode">
<Background />
</VueFlow>
</div>
A default node includes two handles and serves as a branching junction in your map.
A default node comes with two handles.
It represents a branching point in your map.
You have the freedom to determine the location of handles in the node's definition.
You can specify the position of handles in the node definition.
```ts
import { ref } from 'vue'
import { Position } from '@vue-flow/core'
```js{5-6}
const nodes = [
const nodes = ref([
{
id: '1',
label: 'Node 1',
label: 'Default Node',
type: 'default', // You can omit this as it's the fallback type
targetHandle: Position.Top, // or Bottom, Left, Right,
sourceHandle: Position.Right,
sourceHandle: Position.Bottom, // or Top, Left, Right,
}
]
])
```
<div class="mt-4 bg-[var(--vp-code-block-bg)] rounded-lg h-50">
<VueFlow v-model="defaultNode">
<Background class="rounded-lg" />
</VueFlow>
</div>
### Input Node
<div class="mt-4 bg-[var(--vp-code-block-bg)] rounded h-50">
An input node features a single handle, which is by default positioned at the bottom.
It represents a starting point of your map.
```ts
import { ref } from 'vue'
import { Position } from '@vue-flow/core'
const nodes = ref([
{
id: '1',
label: 'Input Node',
type: 'input',
sourceHandle: Position.Bottom, // or Top, Left, Right,
}
])
```
<div class="mt-4 bg-[var(--vp-code-block-bg)] rounded-lg h-50">
<VueFlow v-model="inputNode">
<Background />
<Background class="rounded-lg" />
</VueFlow>
</div>
An input node has a single handle, located at the bottom by default.
It represents a starting point of your map.
### Output Node
<div class="mt-4 bg-[var(--vp-code-block-bg)] rounded h-50">
An output node also possesses a single handle, although it is typically found at the top.
This node represents a conclusion point of your map.
```ts
import { ref } from 'vue'
import { Position } from '@vue-flow/core'
const nodes = ref([
{
id: '1',
label: 'Output Node',
type: 'output',
targetHandle: Position.Top, // or Bottom, Left, Right,
}
])
```
<div class="mt-4 bg-[var(--vp-code-block-bg)] rounded-lg h-50">
<VueFlow v-model="outputNode">
<Background />
<Background class="rounded-lg" />
</VueFlow>
</div>
An output node has a single handle, located at the top by default.
It represents an ending point of your map.
## Custom Nodes
## User-Defined Nodes
In addition to the default node types from the previous chapter, you can define any amount of custom node-types.
Node-types are inferred from your node's definition.
On top of the default node types mentioned earlier, you can create as many custom node-types as you need.
Node-types are determined from your nodes' definitions.
```js{5,11}
const nodes = [
::: code-group
```js [nodes <LogosJavascript />]
import { ref } from 'vue'
export const nodes = ref([
{
id: '1',
label: 'Node 1',
// this will create the node-type `custom`
type: 'custom',
position: { x: 50, y: 50 },
},
{
id: '1',
label: 'Node 1',
// this will create the node-type `special`
type: 'special',
position: { x: 150, y: 50 },
}
]
])
```
Vue Flow will now try to resolve this node-type to a component.
First and foremost we will look for a definition in the `nodeTypes` object of the state.
After that we will try to resolve the component to a globally registered one that matches the exact name.
Finally, we will check if a template slot has been provided to fill the node-type.
```vue [CustomNode.vue <LogosJavascript />]
<script setup>
import { Position } from '@vue-flow/core'
If none of these methods succeed in resolving the component the default node-type will be used as a fallback.
const props = defineProps(['label'])
</script>
<template>
<div>
<Handle type="target" :position="Position.Top" />
<div>{{ label }}</div>
<Handle type="source" :position="Position.Bottom" />
</div>
</template>
```
```ts [nodes <LogosTypescript />]
import { ref } from 'vue'
import type { Node } from '@vue-flow/core'
// You can pass 3 optional generic arguments to the Node interface, allowing you to define:
// 1. The data object type
// 2. The events object type
// 3. The possible node types
export interface CustomData {
hello: string
}
export interface CustomEvents {
onCustomEvent: (event: MouseEvent) => void
}
type CustomNodeTypes = 'custom' | 'special'
type CustomNode = Node<CustomData, CustomEvents, CustomNodeTypes>
export const nodes = ref<CustomNode[]>([
{
id: '1',
label: 'Node 1',
// this will create the node-type `custom`
type: 'custom',
position: { x: 50, y: 50 },
},
{
id: '1',
label: 'Node 1',
// this will create the node-type `special`
type: 'special',
position: { x: 150, y: 50 },
},
// this will throw a type error, as the type is not defined in the CustomEdgeTypes
// regardless it would be rendered as a default edge type
{
id: '1',
label: 'Node 1',
type: 'invalid',
position: { x: 150, y: 50 },
}
])
```
```vue [CustomNode.vue <LogosTypescript />]
<script setup lang="ts">
import type { NodeProps } from '@vue-flow/core'
import { Position } from '@vue-flow/core'
import { CustomData, CustomEvents } from './nodes'
const props = defineProps<NodeProps<CustomData, CustomEvents>>()
console.log(props.data.hello) // 'world'
</script>
<template>
<div>
<Handle type="target" :position="Position.Top" />
<div>{{ label }}</div>
<Handle type="source" :position="Position.Bottom" />
</div>
</template>
```
:::
Vue Flow will then attempt to resolve this node-type to a component.
Priority is given to a definition in the nodeTypes object of the state.
Next, it tries to match the component to a globally registered one with the same name.
Finally, it searches for a provided template slot to fill in the node-type.
If no methods produce a result in resolving the component, the default node-type is used as a fallback.
### Template slots
The easiest way to define custom nodes is, by passing them as template slots.
Your custom node-types are dynamically resolved to slot-names, meaning a node with the type `custom`
will expect a slot to have the name `node-custom`.
One of the easiest ways to define custom nodes is, by passing them as template slots.
Dynamic resolution to slot-names is done for your user-defined node-types,
meaning a node with the type `custom` is expected to have a slot named `#node-custom`.
```vue{9,16}
```vue{9,18}
<script setup>
import { VueFlow } from '@vue-flow/core'
import CustomNode from './CustomNode.vue'
@@ -225,8 +443,10 @@ const elements = ref([
}
])
</script>
<template>
<VueFlow v-model="elements">
<!-- the expected slot name is `node-custom` -->
<template #node-custom="props">
<CustomNode v-bind="props" />
</template>
@@ -236,13 +456,10 @@ const elements = ref([
### Node-types object
You can also define node-types by passing an object as a prop to the VueFlow component (or as an option to the
composable).
Alternatively, node-types can also be defined by passing an object as a prop to the VueFlow component (or as an option to the composable).
::: warning
When doing this, mark your components as raw (using the designated function from the vue library) to avoid them being
turned into reactive objects.
Otherwise, vue will throw a warning in the console.
::: warning
Take precaution to mark your components as raw (utilizing the marked function from the Vue library) to prevent their conversion into reactive objects. Otherwise, Vue will display a warning on the console.
:::
```vue{6-9,26}
@@ -269,60 +486,20 @@ const elements = ref([
}
])
</script>
<template>
<div style="height: 300px">
<VueFlow v-model="elements" :node-types="nodeTypes" />
</div>
<VueFlow v-model="elements" :node-types="nodeTypes" />
</template>
```
::: tip
You can find a more advanced example [here](/examples/nodes/).
[You can find a working example here](/examples/nodes/).
:::
### Node Template
## [Node Props](/typedocs/interfaces/NodeProps)
You can also set a template per node, which will overwrite the node-type component but will retain
the type otherwise.
```vue{17}
<script setup>
import { markRaw } from 'vue'
import CustomNode from './CustomNode.vue'
import OverwriteCustomNode from './OverwriteCustomNode.vue'
import SpecialNode from './SpecialNode.vue'
const nodeTypes = {
custom: markRaw(CustomNode),
special: markRaw(SpecialNode),
}
const elements = ref([
{
id: '1',
label: 'Node 1',
type: 'custom',
template: markRaw(OverwriteCustomNode),
},
{
id: '1',
label: 'Node 1',
type: 'special',
}
])
</script>
<template>
<div style="height: 300px">
<VueFlow v-model="elements" :node-types="nodeTypes" />
</div>
</template>
```
### [(Custom) Node Props](/typedocs/interfaces/NodeProps)
Your custom nodes are wrapped so that the basic functions like dragging or selecting work.
But you might want to extend on that functionality or implement your own business logic inside of nodes, therefore
your nodes receive the following props:
Your custom nodes are enclosed so that fundamental functions like dragging or selecting operate.
But you may wish to expand on these features or implement your business logic inside nodes, thus your nodes receive the following properties:
| Name | Definition | Type | Optional |
|------------------|--------------------------------------------------|------------------------------------------------------------|--------------------------------------------|
@@ -344,129 +521,240 @@ your nodes receive the following props:
| sourcePosition | Source handle position | [Position](/typedocs/enums/Position) | <Check class="text-[var(--vp-c-brand)]" /> |
| dragHandle | Node drag handle class | string | <Check class="text-[var(--vp-c-brand)]" /> |
### (Custom) Node Events
## [Node Events](/typedocs/types/NodeEventsHandler)
In addition to the event handlers that you can access through [`useVueFlow`](/guide/composables#useVueFlow/) or the Vue
Flow component,
you can also pass in event handlers in your initial node definition, or you can access the node events through
the `events` prop passed
to your node components.
Vue Flow provides two main ways of listening to node events,
either by using `useVueFlow` to bind listeners to the event handlers
or by using binding listeners to the `<VueFlow>` component.
```vue{10-17}
::: code-group
```vue [useVueFlow]
<script setup>
import { ref } from 'vue'
import { VueFlow, useVueFlow } from '@vue-flow/core'
// useVueFlow provides access to the event handlers
const {
onNodeDragStart,
onNodeDrag,
onNodeDragStop,
onNodeClick,
onNodeDoubleClick,
onNodeContextMenu,
onNodeMouseEnter,
onNodeMouseLeave,
onNodeMouseMove
} = useVueFlow()
const elements = ref([
{
id: '1',
label: 'Node 1',
position: { x: 50, y: 50 },
},
])
// bind listeners to the event handlers
onNodeDragStart((event) => {
console.log('Node drag started', event)
})
onNodeDrag((event) => {
console.log('Node dragged', event)
})
onNodeDragStop((event) => {
console.log('Node drag stopped', event)
})
// ... and so on
</script>
<template>
<VueFlow v-model="elements" />
</template>
```
```vue [component]
<script setup>
import { ref } from 'vue'
import { VueFlow } from '@vue-flow/core'
const elements = ref([
{
id: '1',
label: 'Node 1',
type: 'custom',
position: { x: 50, y: 50 },
events: {
click: () => {
console.log('Node 1 clicked')
},
customEvent: () => {
console.log('Node 1 custom event')
},
}
}
},
])
</script>
```
As you can see above, you can also pass in custom event handlers. These will not be called by Vue Flow but can be used
to forward callback functions to your custom components.
The `click` handler is part of the [`NodeEventsHandler`](/typedocs/types/NodeEventsHandler) interface, meaning it
will be
triggered when the node is clicked.
```vue
<script lang="ts" setup>
import type { NodeProps, NodeEventsOn } from '@vue-flow/core'
// define your events
interface CustomNodeEvents {
click: NodeEventsOn['click']
customEvent: (input: string) => void
}
interface CustomNodeProps extends NodeProps<any, CustomNodeEvents> {
id: string
events: CustomNodeEvents
}
const props = defineProps<CustomNodeProps>()
props.events.click(() => {
console.log(`Node ${props.id} clicked`)
})
// custom events are just functions, they are not hooks which you can listen to like `click`
props.events.customEvent('custom event triggered')
</script>
<template>
<!-- Omitted for simplicty -->
<!-- bind listeners to the event handlers -->
<VueFlow
v-model="elements"
@node-drag-start="console.log('drag start', $event)"
@node-drag="console.log('drag', $event)"
@node-drag-stop="console.log('drag stop', $event)"
@node-click="console.log('click', $event)"
@node-double-click="console.log('dblclick', $event)"
@node-contextmenu="console.log('contextmenu', $event)"
@node-mouse-enter="console.log('mouseenter', $event)"
@node-mouse-leave="console.log('mouseleave', $event)"
@node-mouse-move="console.log('mousemove', $event)"
/>
</template>
```
## Styling
::: tip
To overwrite default theme styles check the [Theming section](/guide/theming).
:::
### Custom Nodes
<div class="mt-4 bg-[var(--vp-code-block-bg)] rounded-lg h-50">
<VueFlow
v-model="defaultNode"
@node-drag-start="console.log('drag start', $event)"
@node-drag="console.log('drag', $event)"
@node-drag-stop="console.log('drag stop', $event)"
@node-click="console.log('click', $event)"
@node-double-click="console.log('dblclick', $event)"
@node-contextmenu="console.log('contextmenu', $event)"
@node-mouse-enter="console.log('mouseenter', $event)"
@node-mouse-leave="console.log('mouseleave', $event)"
@node-mouse-move="console.log('mousemove', $event)"
>
<Panel position="top-center">
<p class="text-sm">Interact to see events in browser console</p>
</Panel>
<Background class="rounded-lg" />
</VueFlow>
</div>
When you create a new node type you also need to implement some styling. Your custom node has no default styles.
## Customizing Appearance
::: tip
To override the styles of the default theme, visit the [Theming section](/guide/theming).
:::
### User-Defined Nodes
When constructing a new node type, it's necessary for you to add some styling specific to it.
User-created nodes don't have any default styles associated and thus need custom styling.
```css
.vue-flow__node-custom {
background: #9CA8B3;
color: #fff;
padding: 10px;
background: #9CA8B3;
color: #fff;
padding: 10px;
}
```
## Scrolling inside a node
## Implementing Scrolling within Nodes
Sometimes, a node might contain a large amount of content, making it difficult for users to view everything without the aid of a scroll function.
To facilitate this scrolling ability without invoking zoom or pan behaviors on the node, Vue Flow provides the `noWheelClassName` property.
The `noWheelClassName` property allows you to specify a class name that, when applied to a node, will disable the default zoom-on-scroll or pan-on-scroll events on that particular node.
You can use the `noWheelClassName` prop to define a class name which will prevent zoom-on-scroll or pan-on-scroll behavior on
that node.
By default, the `noWheelClassName` is `nowheel`.
By adding this class you can enable scrolling inside a node without triggering zoom-pan behavior.
## Dragging inside a node
You can use the `noDragClassName` prop to define a class name which will not trigger dragging behavior on
that node.
By default, the `noDragClassName` is `nodrag`.
## Dynamic handle positions / Adding handles dynamically
::: info
When using Vue Flow 1.x you don't need to call `updateNodeInternals` when adding handles dynamically.
Handles will try to be added to the node automatically when they are mounted.
If this does not work for you, for whatever reason, you can still follow the guide below and force Vue Flow to update
the node internals.
:::
When working with dynamic handle positions or adding handles dynamically, you need to use
the [`updateNodeInternals`](/typedocs/types/UpdateNodeInternals) method.
You need to call this method otherwise your node will not respond to the new handles and edges will be
misaligned.
You can either use the store action to update multiple nodes at once by passing their ids into the method,
or you can emit the `updateNodeInternals` event from your custom node component without passing any parameters.
### Examples
- Using store action
```vue
<script setup>
import { ref } from 'vue'
const listItems = ref(Array.from({ length: 100 }, (_, i) => i))
</script>
<template>
<div class="custom-node-container">
<ul class="nowheel">
<li v-for="item in listItems" :key="item">Item {{ item }}</li>
</ul>
</div>
</template>
```
<div class="mt-4 bg-[var(--vp-code-block-bg)] rounded-lg h-50">
<VueFlow :model-value="[{ id: '1', type: 'scrollable', label: 'Node 1', position: { x: 50, y: 50 } }]">
<template #node-scrollable>
<ScrollableNode />
</template>
<Background class="rounded-lg" />
</VueFlow>
</div>
<style>
.vue-flow__node-scrollable {
@apply bg-accent rounded px-2;
overflow: auto;
width: 150px;
}
.custom-node-container ul {
max-height: 75px;
}
</style>
## Preventing Drag Behavior withing Nodes
There are certain scenarios where you might need to interact with the contents of a node without triggering a drag action on the node itself.
This can be particularly useful when nodes contain interactive elements like input boxes, buttons, or sliders that you want your users to engage with.
To accomplish this, Vue Flow provides a `noDragClassName` property.
This property allows specification of a class name, which when applied to an element within a node,
prevents triggering a drag action on the node when the user interacts with that element.
By default, the `noDragClassName` is set as `nodrag`.
```vue
<script setup>
import { ref } from 'vue'
const inputValue = ref('')
</script>
<template>
<div class="custom-node-container">
<input class="nodrag" v-model="inputValue" />
</div>
</template>
```
<div class="mt-4 bg-[var(--vp-code-block-bg)] rounded-lg h-50">
<VueFlow :model-value="[{ id: '1', type: 'input-field', label: 'Node 1', position: { x: 50, y: 50 } }]">
<template #node-input-field>
<InputFieldNode />
</template>
<Background class="rounded-lg" />
</VueFlow>
</div>
<style>
.vue-flow__node-input-field {
@apply bg-primary rounded p-4;
}
</style>
## Working with Dynamic Handle Positions / Adding Handles Dynamically
::: tip
In Vue Flow 1.x, there's no need to manually invoke `updateNodeInternals` when dynamically adding handles.
Upon mounting, handles will automatically attempt to attach to the node.
However, if for any reason this isn't happening as expected, you can stick to the guideline provided below to enforce Vue Flow to update the node internals.
:::
At times, you may need to modify handle positions dynamically or programmatically add new handles to a node. In this scenario, the [`updateNodeInternals`](/typedocs/types/UpdateNodeInternals) method found in Vue Flow's API comes in handy.
Invoking this method is vital when dealing with dynamic handles. If not, the node might fail to recognize these new handles, resulting in misaligned edges.
The `updateNodeInternals` function can be deployed in one of two ways:
- **Using the store action:** This approach allows you to update several nodes at once by passing their IDs into the method.
- **Emitting the `updateNodeInternals` event from your customized node component:** This doesn't require any parameters to be passed.
::: code-group
```js [store action]
import { useVueFlow } from '@vue-flow/core'
const { updateNodeInternals } = useVueFlow()
@@ -474,12 +762,9 @@ const { updateNodeInternals } = useVueFlow()
const onSomeEvent = () => {
updateNodeInternals(['1'])
}
</script>
```
- Emitting event from custom component
```vue
```vue [emit event]
<script setup>
const emits = defineEmits(['updateNodeInternals'])
@@ -488,3 +773,5 @@ const onSomeEvent = () => {
}
</script>
```
:::