Delete Middle Node
This example shows you how to recover deleted edges when you remove a node from the middle
of a chain. In other words, if we have three nodes connected in sequence - a->b->c
- and
we deleted the middle node b
, this example shows you how to end up with the graph
a->c
.
To achieve this, we need to make use of a few bits:
- The
onbeforedelete
handler lets us intercept and modify the deletion process before nodes are removed. getConnectedEdges
gives us all the edges connected to a node, either as source or target.getIncomers
andgetOutgoers
give us the nodes connected to a node as source or target.
All together, this allows us to take all the nodes connected to the deleted node, and reconnect them to any nodes the deleted node was connected to.
<script lang="ts">
import {
SvelteFlow,
Background,
type Node,
type Edge,
type OnBeforeDelete,
getIncomers,
getOutgoers,
getConnectedEdges,
} from '@xyflow/svelte';
import '@xyflow/svelte/dist/style.css';
import { initialNodes, initialEdges } from './nodes-and-edges';
let nodes = $state.raw<Node[]>(initialNodes);
let edges = $state.raw<Edge[]>(initialEdges);
const onbeforedelete: OnBeforeDelete = async ({ nodes: deletedNodes, edges: _edges }) => {
let remainingNodes = [...nodes];
edges = deletedNodes.reduce((acc, node) => {
const incomers = getIncomers(node, remainingNodes, acc);
const outgoers = getOutgoers(node, remainingNodes, acc);
const connectedEdges = getConnectedEdges([node], acc);
const remainingEdges = acc.filter((edge) => !connectedEdges.includes(edge));
const createdEdges = incomers.flatMap(({ id: source }) =>
outgoers.map(({ id: target }) => ({
id: `${source}->${target}`,
source,
target,
})),
);
remainingNodes = remainingNodes.filter((rn) => rn.id !== node.id);
return [...remainingEdges, ...createdEdges];
}, edges);
nodes = remainingNodes;
return true;
};
</script>
<SvelteFlow bind:nodes bind:edges {onbeforedelete} fitView>
<Background />
</SvelteFlow>
Although this example is less than 20 lines of code there’s quite a lot to digest. Let’s break some of it down:
-
Our
onbeforedelete
handler is called with an object containingnodes
andedges
arrays representing what will be deleted. Thenodes
array contains every node that will be deleted. If you select an individual node and press the delete key, it will contain just that node, but if you make a selection all the nodes in that selection will be included. -
We create a new array of edges -
remainingEdges
- that contains all the edges in the flow that have nothing to do with the node(s) we’re about to delete. -
We create another array of edges by flatMapping over the array of
incomers
. These are nodes that were connected to the deleted node as a source. For each of these nodes, we create a new edge that connects to each node in the array ofoutgoers
. These are nodes that were connected to the deleted node as a target. -
Finally, we return
true
from the callback to allow the deletion to proceed with our modified edge array.
For brevity, we’re using object destructuring while at the same time renaming
the variable bound (e.g. ({ id: source }) => ...)
destructures the id
property of the object and binds it to a new variable called source
) but you
don’t need to do this