JavaScript is required

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:

  1. Clear current graph data.
  2. Load nodes, lines, and fakeLines.
  3. Flatten tree-shaped children into flat nodes and lines.
  4. Set rootId.
  5. 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:

  1. Calls setJsonData(data).
  2. Calls moveToCenter().
  3. 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, and edges are compatible aliases for lines, but warnings are printed.
  • elementLines is deprecated. Use fakeLines + 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:

  • addNode receives one JsonNode.
  • addNodes receives an array.
  • Existing node ids are skipped and not overwritten.
  • If a node has no text, the source code may fall back to label or id; new code should explicitly set text.

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/to must exist.
  • Duplicate id values are skipped.
  • Missing id can be generated by the source code, but that is not ideal for persistence.
  • source/target/label are 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: call doLayout() after append.
  • false: only append data and do not relayout automatically; suitable when you set x/y yourself.

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.
  • editingNodes comes 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:

  • updateNode updates node properties.
  • updateNodePosition is dedicated to coordinate changes; when a force layout is running, it also syncs the node-position cache in the layout engine.
  • updateNodeData merges node.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:

  • moveToCenter only moves the canvas center and does not change zoom.
  • zoomToFit moves and zooms so the target content is as fully visible as possible.
  • setZoom uses a percentage value; 100 means 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:

  1. Initialize with setJsonData() or applyInitialData().
  2. Use addNode() when users create nodes.
  3. Use addLines() or addFakeLines() when users create lines.
  4. Use updateNode() / updateLine() / updateFakeLine() from property panels.
  5. Call doLayout() only when the user explicitly clicks “auto layout”.
  6. Save with getGraphJsonData().

Avoid:

  • Calling setJsonData() after every edit.
  • Directly replacing a reactive options object.
  • Directly mutating RGNode/RGLine/RGLink without using instance APIs.

Read-Only Display Apps

Recommended:

  1. Load complete data with applyInitialData().
  2. For filtering, update hidden or set data again.
  3. Use focusNodeById(), moveToCenter(), and zoomToFit() when focusing nodes.
  4. 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.

11. Next