JavaScript is required

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 pageSize and fromIndex parameters
  • 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.