← Back to list

VTable Master-Detail Table Plugin

Author of this article:

Xuanhun · 2025-10-17 10:09 · 6 claps · 7.0 min read
#data-visualization #grid #tableau #open-source #front-end-development
Open on Medium ↗
Wiki topics: VIS · Visual & Graphic Design LIT · Literature & Writing 🔓 · Open Source

VTable Master-Detail Table Plugin

Author of this article:

Potato Chips (https://github.com/Violet2314) Guangdong University of Finance & Economics

Introduction

The VTable Master-Detail Table Plugin (MasterDetailPlugin) is an enterprise-level data visualization component developed based on VisActor VTable, specifically designed to solve the needs of hierarchical data display in complex business scenarios. This plugin breaks through the flat limitations of traditional tables and implements an innovative interaction mode of embedding complete sub-tables within master table rows, providing users with an intuitive and efficient data drill-down and detail viewing experience.

Core Capabilities

  • Supports embedding complete sub-tables within master table rows, making complex data structures clear at a glance
  • Supports both static configuration and dynamic function configuration to meet various business scenarios
  • Supports lazy loading mechanism to optimize performance in large data volume scenarios

Typical Business Scenarios

Business ScenarioMaster Table DataSub-table DataOrder ManagementOrder basic informationProduct list, logistics detailsProject ManagementProject overviewTask list, member assignmentsFinancial ManagementSummary dataDetailed accounts, voucher informationInventory ManagementProduct categoriesSKU details, inventory dynamicsCustomer ManagementCustomer basic informationContact records, transaction history

Quick Start

Get NPM Package

First, you need to install using the following command in your project root directory:

# Install using npm
npm install @visactor/vtable @visactor/vtable-plugins

# Install using yarn
yarn add @visactor/vtable @visactor/vtable-plugins

Import Master-Detail Plugin

Import via NPM package

import * as VTable from '@visactor/vtable';
import { MasterDetailPlugin } from '@visactor/vtable-plugins';

function generateData(count) {
  const depts = ['Engineering', 'Marketing', 'Sales', 'HR', 'Finance'];
  return Array.from({ length: count }).map((_, i) => ({
    id: i + 1,
    rowNo: i + 1,
    name: `Name ${i + 1}`,
    department: depts[i % depts.length],
    score: Math.floor(Math.random() * 100),
    amount: Math.floor(Math.random() * 10000) / 100,
    children:
      i % 4 === 0
        ? [
            { task: `Sub-task A-${i + 1}`, status: 'open' },
            { task: `Sub-task B-${i + 1}`, status: 'done' }
          ]
        : undefined
  }));
}

const records = generateData(11);
const masterDetailPlugin = new VTablePlugins.MasterDetailPlugin({
  id: 'master-detail-static-3',
  detailTableOptions: {
    columns: [
      { field: 'task', title: 'Task Name', width: 220 },
      { field: 'status', title: 'Status', width: 120 }
    ],
    defaultRowHeight: 30,
    defaultHeaderRowHeight: 30,
    style: { margin: 12, height: 160 },
    theme: VTable.themes.BRIGHT
  }
});

const columns = [
  { field: 'id', title: 'ID', width: 70, sort: true },
  { field: 'rowNo', title: '#', width: 60, headerType: 'text', cellType: 'text' },
  { field: 'name', title: 'Name', width: 140, sort: true },
  { field: 'department', title: 'Department', width: 140, sort: true },
  { field: 'score', title: 'Score', width: 100, sort: true },
  {
    field: 'amount',
    title: 'Amount',
    width: 120,
    sort: true,
    fieldFormat: (v) => {
      if (typeof v === 'number' && !isNaN(v)) {
        return `$${v.toFixed(2)}`;
      }
      return v === undefined || v === null ? '' : String(v);
    }
  }
];

const option = {
  container: document.getElementById(CONTAINER_ID),
  columns,
  records,
  autoFillWidth: true,
  hierarchyTextStartAlignment: true,
  plugins: [masterDetailPlugin]
};

const tableInstance = new VTable.ListTable(option);

Running effect:

Parameter Configuration

Parameter NameTypeDefault ValueFunction Descriptionidstringmaster-detail-${timestamp}Global unique identifier for the plugin instance, used to distinguish multiple plugin instancesenableCheckboxCascadebooleantrueWhether to enable checkbox cascade functionality between master and detail tables, checkbox selections in the master table will automatically sync with corresponding sub-tablesdetailTableOptionsDetailTableOptions | Function-Sub-table configuration options, supports static object configuration or dynamic configuration function based on data

Dynamic configuration example:

const masterDetailPlugin = new MasterDetailPlugin({
  id: 'employee-detail-plugin',
  detailTableOptions: ({ data, bodyRowIndex }) => {
    if (bodyRowIndex === 0) {
      return {
        columns: [
          //......
        ],
        theme: VTable.themes.BRIGHT,
        style: {
          margin: 20,
          height: 300
        }
      };
    }
    return {
      columns: [
        //......
      ],
      theme: VTable.themes.DARK,
      style: {
        margin: 20,
        height: 300
      }
    };
  }
});

Main Capabilities of Master-Detail Plugin

Row Expansion and Sub-table Rendering

When a user clicks the expand icon of a table row, the system dynamically creates a complete sub-table instance below that row. The sub-table has independent configuration, data source and interaction capabilities, forming a hierarchical data display structure with the master table.

When a user clicks the expand icon, it triggers the following processing chain:

User clicks expand icon
    ↓
EventManager.handleIconClick()
    ↓ 
MasterDetailPlugin.expandRow(rowIndex, colIndex)
    ↓
Check if row is already expanded (isRowExpanded)
    ↓
Get record data (getRecordByRowIndex)
    ↓
MasterDetailPlugin.getChildren()
    ↓
ConfigManager.getDetailConfigForRecord()
    ↓
updateRowHeightForExpand()
    ↓
updateContainerHeight()
    ↓
SubTableManager.renderSubTable()
    ↓
recalculateAllSubTablePositions(bodyRowIndex + 1)
    ↓
drawUnderlineForRow()
    ↓
refreshRowIcon()

Technical Features

  • Independent Instance: Each sub-table is a complete VTable instance, supporting all table functions
  • Dynamic Row Height: Master table row height automatically adapts to sub-table content, supporting both adaptive and fixed height
  • Position Synchronization: Sub-table positions follow in real-time when the master table scrolls, maintaining visual continuity
  • Memory Management: Automatically destroys sub-table instances when collapsed, recreates when expanded

Lifecycle Management

Creation Phase: Master table row expands → Parse configuration → Instantiate sub-table

Running Phase: Position synchronization → Event handling → Data updates

Destruction Phase: Row collapses → Clean up events → Destroy instance → Reclaim memory

Scroll Synchronization Mechanism

When the master table scrolls, all sub-tables follow the scroll in real-time, maintaining a visually integrated effect. The scroll event trigger mechanism has been optimized, and there is also a master-detail scroll separation mechanism: when the mouse scrolls within the sub-table and has not reached the top or bottom boundary, only the sub-table content scrolls and the master table does not scroll; when the master table scrolls, the sub-table does not scroll.

Technical Features

  • scrollEventAlwaysTrigger: Automatically set to true, ensuring boundary scrolling also triggers events
  • Batch Update: Batch updates all sub-table positions with one scroll event

Master-Detail Checkbox Linkage

Master-detail checkbox linkage implements intelligent synchronization of selection states between master and detail tables. When a master table row is selected, all rows in its corresponding sub-table are automatically selected; when some rows in the sub-table are selected/deselected, the selection state of the master table row updates accordingly. Use the enableCheckboxCascade parameter to determine whether to enable it, default is true.

Master table checkbox click → Detect sub-table existence → Traverse all sub-table rows → Sync selection state → Trigger linkage callback
Sub-table checkbox click → Count sub-table selected quantity → Calculate master table state → Update master table checkbox → Trigger linkage callback

Technical Implementation

  • Event Listening: Monitors checkbox change events in both master and sub-tables
  • State Synchronization: Maintains selection state mapping relationships through internal state manager
  • Callback Notification: Triggers corresponding callback functions when selection state changes, facilitating business processing

This linkage mechanism greatly improves the operational efficiency of complex data structures, allowing users to perform batch selection operations with simple clicks.

Lazy Loading Functionality

When the parent row record’s children field is true, it indicates this is a lazy loading node that requires asynchronous data retrieval by listening to the VTable TREE_HIERARCHY_STATE_CHANGE event.

When a user clicks the expand icon containing a lazy loading node, it triggers the following call chain:

User clicks expand icon
    ↓
table.toggleHierarchyState()
    ↓
Trigger TREE_HIERARCHY_STATE_CHANGE event
    ↓
User event handler receives event parameters
    ↓
tableInstance.setLoadingHierarchyState(col, row) displays loading icon
    ↓
Asynchronous data retrieval
    ↓
plugin.setRecordChildren(detailData, col, row) sets data and expands
    ↓
Renders sub-table and completes expansion

MethodDescriptionParametersplugin.setLoadingHierarchyState(col, row)Display loading iconcol: column index, row: row indexplugin.setRecordChildren(children, col, row)Set child data and expandchildren: child data array, col: column index, row: row index

User Configuration Example

const masterData = [
  { id: 1, name: 'Order001', children: true },    // Lazy loading identifier
  { id: 2, name: 'Order002', children: [...] }   // Static data
];
const plugin = new MasterDetailPlugin({
  detailTableOptions: {
    // Sub-table configuration
  }
});
const tableInstance = new VTable.ListTable(options);
// Listen to master-detail hierarchy state change event
const { MASTER_DETAIL_HIERARCHY_STATE_CHANGE } = VTable.ListTable.EVENT_TYPE;
tableInstance.on(MASTER_DETAIL_HIERARCHY_STATE_CHANGE, async (args) => {
  if (args.hierarchyState === VTable.TYPES.HierarchyState.expand && 
      args.originData?.children === true) {

    // Show loading state
    plugin.setLoadingHierarchyState(args.col, args.row);

    try {
      // Asynchronous data retrieval
      const detailData = await fetchDataFromAPI(args.originData.id);

      // Set child data and automatically expand
      plugin.setRecordChildren(detailData, args.col, args.row);
    } catch (error) {
      console.error('Failed to load detail data:', error);
    }
  }
});

Intelligent Caching Mechanism

  • One-time Loading: After successful data loading, children: true automatically converts to actual data array
  • Permanent Cache: When clicked again, it is recognized as static data and expands directly without reloading

This design implements transparent on-demand loading. Users only need to configure the onLazyLoad callback, and the rest of the state management is completely handled automatically by the plugin.

Summary

The VTable Master-Detail Table Plugin is a growing open source data display plugin, committed to providing excellent hierarchical data display solutions for web applications.

It brings the capability of displaying complex master-detail data relationships to the web while fully leveraging the interactive advantages of modern web technology. Whether you’re building order management systems, project management platforms, financial reporting systems, or any web application that requires master-detail table functionality, the VTable Master-Detail Plugin can provide you with an excellent choice.

Online Demo and Tutorials

Demo: https://visactor.com/vtable/demo/table-type/list-table-master-detail-table

Tutorial: https://visactor.com/vtable/guide/plugin/master-detail

Welcome to Connect

Finally, we sincerely welcome all friends interested in data visualization to participate in VisActor’s open source development:

VTable: VTable Official Website, VTable Github (Welcome to Star)

VisActor Official Website: www.visactor.io/ or www.viactor.com

Discord: discord.gg/3wPyxVyH6m

Feishu Group (External Network): Open Link to Scan QR Code

WeChat Official Account: Open Link to Scan QR Code

GitHub: github.com/VisActor


메타데이터
post_id
47fc16dd8a3a
slug
vtable-master-detail-table-plugin-47fc16dd8a3a
url
https://medium.com/@xuanhun9/vtable-master-detail-table-plugin-47fc16dd8a3a
canonical_url
https://medium.com/@xuanhun9/vtable-master-detail-table-plugin-47fc16dd8a3a
author_url
https://medium.com/@xuanhun9
status
ok
fetched_at
2026-07-16 17:42:09