Graph Data Flow And CRUD
This page explains how data enters relation-graph, how it becomes runtime objects, how to query, update, export it, and which operations trigger layout or view changes.
The relation-graph data flow can be summarized as:
JsonNode / JsonLine / fakeLines
-> data provider conversion
-> RGNode / RGLine / RGFakeLine
-> runtime indexes and RGLink
-> visibility calculation / layout / rendering
1. Three Main Data Entry Methods
Method A: setJsonData Replaces The Whole Graph
await graphInstance.setJsonData({
rootId: 'root',
nodes: [
{ id: 'root', text: 'Root' },
{ id: 'a', text: 'Node A' }
],
lines: [
{ id: 'root-a', from: 'root', to: 'a', text: 'Relation' }
],
fakeLines: []
});
Actual flow:
- Clear current graph data.
- Load
nodes,lines, andfakeLines. - Flatten tree-shaped
childreninto flat nodes and lines. - Set
rootId. - Call
doLayout(rootId).
Suitable for:
- First loading a complete graph.
- Switching to another complete data set.
- Automatically relayouting with the current
options.layout.
Not suitable for:
- High-frequency editor operations.
- Repeatedly refreshing the whole graph while the user is dragging, zooming, or editing locally.
- Updating only one or two fields.
Method B: applyInitialData Initializes And Fits The View
await graphInstance.applyInitialData(data);
Actual flow:
- Calls
setJsonData(data). - Calls
moveToCenter(). - Calls
zoomToFit().
This is suitable for first-screen display of a complete graph so users can see all content when entering the page.
Method C: Incremental APIs
graphInstance.addNodes([{ id: 'b', text: 'Node B' }]);
graphInstance.addLines([{ id: 'a-b', from: 'a', to: 'b' }]);
graphInstance.updateNode('b', { color: '#dcfce7' });
graphInstance.updateLine('a-b', { text: 'New relationship' });
Suitable for:
- Graph editors.
- Streaming loading.
- Partial updates.
- Keeping the current view stable during user interaction.
Incremental APIs do not automatically perform full centering or zoom fitting. Call these manually when needed:
await graphInstance.doLayout();
graphInstance.moveToCenter();
graphInstance.zoomToFit();
Method D: Component initialData
The component prop initialData is suitable for passing initial data during component initialization. It is not the recommended reactive update channel.
When business data changes and you need to update the graph, prefer:
setJsonData(): replace the whole graph.appendJsonData(): append a batch of graph data.add/update/remove: partial updates.
2. RGJsonData Structure
type RGJsonData = {
rootId?: string;
nodes: JsonNode[];
lines: JsonLine[];
fakeLines?: JsonLine[];
};
Fields:
| Field | Description |
|---|---|
rootId |
Root node id. It affects the starting point of doLayout(). When unset, the first node is usually used. |
nodes |
Node array. It may include tree-shaped nodes with children. |
lines |
Normal line array. Endpoints must be node ids. |
fakeLines |
Fake line array. Endpoints can be nodes, connection points, HTML elements, or custom objects. |
Historical compatibility:
relations,links, andedgesare compatible aliases forlines, but warnings are printed.elementLinesis deprecated. UsefakeLines+RGInnerConnectTargetType.HTMLElementId.
3. Add Data
Add Nodes
graphInstance.addNode({
id: 'n1',
text: 'Node 1'
});
graphInstance.addNodes([
{ id: 'n2', text: 'Node 2' },
{ id: 'n3', text: 'Node 3', x: 200, y: 100 }
]);
Rules:
addNodereceives oneJsonNode.addNodesreceives an array.- Existing node ids are skipped and not overwritten.
- If a node has no
text, the source code may fall back tolabelorid; new code should explicitly settext.
Add Normal Lines
graphInstance.addLines([
{
id: 'n1-n2',
from: 'n1',
to: 'n2',
text: 'Relation'
}
]);
graphInstance.addLines([
{ id: 'n2-n3', from: 'n2', to: 'n3' }
]);
Rules:
- The nodes referenced by
from/tomust exist. - Duplicate
idvalues are skipped. - Missing
idcan be generated by the source code, but that is not ideal for persistence. source/target/labelare only old-data compatibility aliases and are not recommended for new code.
Add FakeLines
graphInstance.addFakeLines([
{
id: 'node-to-port',
isFakeLine: true,
from: 'n1',
fromType: RGInnerConnectTargetType.Node,
to: 'port-1',
toType: RGInnerConnectTargetType.NodePoint,
text: 'Port connection'
}
]);
You can also add an object with isFakeLine: true through addLines; the source code dispatches it into FakeLine.
Append One RGJsonData Batch
await graphInstance.appendJsonData(
{
nodes: [{ id: 'new-node', text: 'New node' }],
lines: [{ id: 'root-new', from: 'root', to: 'new-node' }]
},
true
);
The second argument isRelayout:
true: calldoLayout()after append.false: only append data and do not relayout automatically; suitable when you setx/yyourself.
4. Query Data
Basic Queries
const options = graphInstance.getOptions();
const nodes = graphInstance.getNodes();
const lines = graphInstance.getLines();
const fakeLines = graphInstance.getFakeLines();
const links = graphInstance.getLinks();
const node = graphInstance.getNodeById('n1');
const line = graphInstance.getLineById('n1-n2');
const fakeLine = graphInstance.getFakeLineById('fake-1');
const link = graphInstance.getLinkByLineId('n1-n2');
Checked And Editing State Queries
const checkedNode = graphInstance.getCheckedNode();
const checkedLine = graphInstance.getCheckedLine();
const selectedNodes = graphInstance.getSelectedNodes();
const editingNodes = graphInstance.getEditingNodes();
Notes:
- Checked means the current focused item, usually one node or one line.
- Selected is a field on the object and can be used for multi-select.
editingNodescomes from the editing-controller state.
Relationship Queries
const node = graphInstance.getNodeById('n1');
const relatedLines = graphInstance.getRelatedLinesByNode(node);
const relatedNodes = graphInstance.getNodeRelatedNodes(node);
const incomingNodes = graphInstance.getNodeIncomingNodes(node);
const outgoingNodes = graphInstance.getNodeOutgoingNodes(node);
const networkNodes = graphInstance.getNetworkNodesByNode(node);
const descendantNodes = graphInstance.getDescendantNodes(node);
Direction filter:
const onlyIncoming = graphInstance.getNodeRelatedNodes(node, {
incoming: true,
outgoing: false
});
Relationships among a set of nodes:
const linksBetween = graphInstance.getLinksBetweenNodes([nodeA, nodeB, nodeC]);
const linesBetween = graphInstance.getLinesBetweenNodes([nodeA, nodeB, nodeC]);
Spatial Queries
const box = graphInstance.getNodesRectBox();
// { width, height, minX, minY, maxX, maxY }
const center = graphInstance.getNodesCenter();
// { x, y }
const nodesInSelection = graphInstance.getNodesInSelectionView(selectionView);
getNodesInSelectionView is usually used together with onCanvasSelectionEnd.
5. Update Data
Update Nodes
graphInstance.updateNode('n1', {
text: 'New name',
color: '#eff6ff',
borderColor: '#2563eb'
});
graphInstance.updateNodePosition('n1', 300, 160);
graphInstance.updateNodeData('n1', {
status: 'online'
});
Notes:
updateNodeupdates node properties.updateNodePositionis dedicated to coordinate changes; when a force layout is running, it also syncs the node-position cache in the layout engine.updateNodeDatamergesnode.data, and is suitable for business fields.
Update Normal Lines
graphInstance.updateLine('n1-n2', {
text: 'Updated relationship',
color: '#f97316',
lineWidth: 3
});
graphInstance.updateLineData('n1-n2', {
weight: 10
});
Update FakeLines
graphInstance.updateFakeLine('fake-1', {
text: 'New fake line',
lineShape: RGLineShape.StandardOrthogonal
});
Update Options
graphInstance.updateOptions({
wheelEventAction: 'scroll',
dragEventAction: 'selection'
});
setOptions and updateOptions are both runtime option-update methods. When layout is provided, the source code merges it with the current options.layout and syncs it to the current layouter.
6. Delete And Clear
Delete Nodes
graphInstance.removeNodeById('n1');
graphInstance.removeNodesByIds(['n2', 'n3']);
graphInstance.removeNode(node);
graphInstance.removeNodes([nodeA, nodeB]);
Delete Normal Lines
graphInstance.removeLineById('line-1');
graphInstance.removeLineByIds(['line-2', 'line-3']);
graphInstance.removeLine(line);
graphInstance.removeLines([lineA, lineB]);
Delete FakeLines
graphInstance.removeFakeLineById('fake-1');
graphInstance.removeFakeLine(fakeLine);
graphInstance.clearFakeLines();
Clear The Graph
graphInstance.clearGraph();
clearGraph() clears:
- All nodes.
- All normal lines.
- All fake lines.
- The root node.
- Checked state.
- Editing node/line state.
7. Layout And View Control
Relayout
await graphInstance.doLayout();
await graphInstance.doLayout('root-node-id');
await graphInstance.doLayout(rootNode);
doLayout:
- Creates or uses a layouter based on the current
options.layout. - Uses the provided root node, current root, or the first node as root.
- Calls
updateNodesVisibleProperty(). - Temporarily enables node-coordinate animation for non-force layouts.
- Handles other disconnected node groups according to
options.placeOtherGroup.
Move And Zoom The View
graphInstance.moveToCenter();
graphInstance.moveToCenter([nodeA, nodeB]);
graphInstance.zoomToFit();
graphInstance.zoomToFit([nodeA, nodeB]);
graphInstance.setZoom(120);
graphInstance.zoom(-10);
graphInstance.setCanvasCenter(0, 0);
Notes:
moveToCenteronly moves the canvas center and does not change zoom.zoomToFitmoves and zooms so the target content is as fully visible as possible.setZoomuses a percentage value;100means 100%.- Zoom is limited by
minCanvasZoom/maxCanvasZoom.
8. Export And Convert
Export Current Graph Data
const jsonData = graphInstance.getGraphJsonData();
Returns:
{
rootId: 'root',
nodes: [...],
lines: [...],
fakeLines: [...]
}
The default export is relatively compact and omits some default values and runtime fields.
Convert Runtime Objects
const nodeJson = graphInstance.transRGNodeToJsonObject(node);
const lineJson = graphInstance.transRGLineToJsonObject(line);
const linkLineJson = graphInstance.transRGLinkToJsonObject(link);
To include resolved defaults:
const effectiveNodeJson = graphInstance.transRGNodeToJsonObject(node, {
mode: 'effective'
});
const effectiveLineJson = graphInstance.transRGLineToJsonObject(line, {
mode: 'effective'
});
9. Recommended Dynamic-App Flows
Editor-Style Apps
Recommended:
- Initialize with
setJsonData()orapplyInitialData(). - Use
addNode()when users create nodes. - Use
addLines()oraddFakeLines()when users create lines. - Use
updateNode()/updateLine()/updateFakeLine()from property panels. - Call
doLayout()only when the user explicitly clicks “auto layout”. - Save with
getGraphJsonData().
Avoid:
- Calling
setJsonData()after every edit. - Directly replacing a reactive
optionsobject. - Directly mutating
RGNode/RGLine/RGLinkwithout using instance APIs.
Read-Only Display Apps
Recommended:
- Load complete data with
applyInitialData(). - For filtering, update
hiddenor set data again. - Use
focusNodeById(),moveToCenter(), andzoomToFit()when focusing nodes. - Use image-export APIs or
getGraphJsonData()when exporting.
10. Common Pitfalls
Why Did The UI Not Update After I Pushed Into An Array Directly?
Do not push directly into arrays returned by getNodes(). Use instance APIs such as addNodes() and updateNode() so the graph can sync indexes, relationships, visibility, and rendering state.
Why Does The View Jump After setJsonData()?
setJsonData() clears and relayouts the whole graph. Editor scenarios should prefer incremental APIs.
Why Is Layout Position Wrong After Appending Nodes?
Node sizes may not have finished DOM measurement yet. The source code waits briefly when calling doLayout() after adding nodes, but complex custom nodes should still wait until node size is stable before layout.
Why Are Child Nodes Or Lines Wrong After Hiding Nodes?
If you directly batch-modify runtime object fields, call:
graphInstance.updateNodesVisibleProperty();
graphInstance.dataUpdated();
It is better to modify through updateNode() and let the graph refresh automatically.