Display More Nodes Button 2
Pagination-Based Node Loading - Dynamically load child nodes in chunks with “Load More” functionality
Pagination-Based Node Loading
Functional Overview
This example demonstrates a sophisticated pagination mechanism for dynamically loading child nodes in a tree-structured relationship graph. When a category node is expanded, it loads a fixed number of child nodes (configurable via pageSize) and provides a “Load More” button to fetch additional data from the server. This approach optimizes initial rendering performance while allowing users to progressively load large datasets on demand.
Implementation of Key Features
Dynamic Data Loading with Pagination
The core pagination logic is implemented in the onNodeExpand event handler and loadNextPageData function:
const onNodeExpand = async (node: RGNode, e: RGUserEvent) => {
if (node.data.childrenLoaded) {
console.log('The children of this node have already been loaded');
return;
}
node.data.childrenLoaded = true;
const myType = node.data.myType;
const fromIndex = 0;
await loadNextPageData(myType, fromIndex);
};
const loadNextPageData = async (myType: string, fromIndex: number) => {
const typeNode = graphInstance.getNodes().find(n => n.data.myType === myType);
const { currentPageItems, total } = await fetchMockDataFromRemoteServer(myType, pageSize, fromIndex);
const currentPageGraphJsonData: RGJsonData = { nodes: [], lines: [] };
for (const entItem of currentPageItems) {
currentPageGraphJsonData.nodes.push({
id: entItem.companyId,
text: entItem.companyName,
data: { ownerType: myType }
});
currentPageGraphJsonData.lines.push({
from: typeNodeId,
to: entItem.companyId
});
}
updateLoadMoreButtonNode(typeNodeId, myType, fromIndex + currentPageItems.length, total, currentPageGraphJsonData);
// Position new nodes at parent location for smooth animation
currentPageGraphJsonData.nodes.forEach((n: JsonNode) => {
n.x = typeNode.x;
n.y = typeNode.y;
});
graphInstance.addNodes(currentPageGraphJsonData.nodes);
graphInstance.addLines(currentPageGraphJsonData.lines);
await graphInstance.sleep(400); // Wait for rendering to complete
await graphInstance.doLayout();
};
Key aspects:
- Lazy loading: Child nodes are only loaded when the parent is expanded
- Server pagination: Data is fetched in chunks using
pageSizeandfromIndexparameters - Smooth positioning: New nodes are positioned at their parent’s coordinates initially for natural expansion animation
- Render waiting: Uses
sleep(400)to ensure nodes are rendered before layout calculation
Dynamic “Load More” Button Management
The updateLoadMoreButtonNode function dynamically adds or removes a special button node based on remaining items:
const updateLoadMoreButtonNode = (
typeNodeId: string,
myType: string,
fromIndex: number,
total: number,
currentPageGraphJsonData: RGJsonData
) => {
const loadNextPageButtonNodeId = `${myType}-next-button`;
graphInstance.removeNodeById(loadNextPageButtonNodeId);
const remainingItemCount = total - fromIndex + 1;
if (remainingItemCount > 0) {
currentPageGraphJsonData.nodes.push({
id: loadNextPageButtonNodeId,
text: `Load More(${remainingItemCount})`,
data: { myType: 'more-btn', loadType: myType, fromIndex: (fromIndex + 1) }
});
currentPageGraphJsonData.lines.push({
from: typeNodeId,
to: loadNextPageButtonNodeId
});
}
};
Custom Node Slot Implementation
The NodeSlot component handles different node types with custom styling and click handlers:
const NodeSlot: React.FC<RGNodeSlotProps & {loadNextPage: (n: RGNode) => void|Promise<void>}> = ({ node, loadNextPage }) => {
const myType = node.data.myType;
const buttonTypes = ['investment-button', 'shareholder-button', 'historical-investment-button', 'historical-shareholder-button'];
return (
<>
{buttonTypes.includes(myType) && (
<div className="my-node my-button-node">{node.text}</div>
)}
{myType === 'root' && (
<div className="my-node my-root">{node.text}</div>
)}
{myType === 'more-btn' && (
<div className="my-node more-btn px-2" onClick={() => {loadNextPage(node)}}>
{node.text}
</div>
)}
{!myType && (
<div className="my-node">{node.text}</div>
)}
</>
);
};
Configurable Page Size
Users can adjust the number of items loaded per page via a number input:
const [pageSize, setPageSize] = React.useState<number>(10);
<SimpleUINumber
currentValue={pageSize}
min={1}
max={200}
onChange={(newValue: number) => { setPageSize(newValue); }}
/>
Tree Layout Configuration
Uses tree layout with orthogonal lines for a clean hierarchical structure:
const graphOptions: RGOptions = {
defaultExpandHolderPosition: 'right',
defaultNodeShape: RGNodeShape.rect,
defaultNodeBorderWidth: 0,
defaultLineShape: RGLineShape.StandardOrthogonal,
defaultPolyLineRadius: 5,
defaultJunctionPoint: RGJunctionPoint.lr,
reLayoutWhenExpandedOrCollapsed: true,
layout: {
layoutName: 'tree',
from: 'left',
treeNodeGapH: 100,
treeNodeGapV: 10,
alignItemsX: 'start',
alignParentItemsX: 'end',
}
};
Creative Use Cases
Corporate Investment Network Analysis: Load investment relationships progressively for large corporations with thousands of subsidiaries and investments. Start with recent investments, then load historical data on demand using pagination.
Social Network Connections: Display a user’s connections with pagination, loading friends/followers in batches of 50-100. The “Load More” button shows remaining connection count, allowing users to explore large networks without initial performance overhead.
Genealogy Tree Visualization: Render family trees where each ancestor node can have hundreds of descendants. Use pagination to load direct children first, then progressively load grandchildren and further descendants as users explore different branches.
E-commerce Product Relations: Show related products for a catalog item with category-based pagination. Load products from each category (e.g., “Accessories”, “Similar Products”) in batches, displaying remaining count in the load button.
Document Reference Graph: Visualize citation networks in academic papers or legal documents. Start with direct references, then load indirect citations page by page, allowing researchers to explore complex webs of document relationships.