To render each subordinate on a new row when the parent row is expanded, we can use the expandedRowRender function to create a sub-table that places each child in its own row. This way, each subordinate will appear on a new line within the expanded area, with correct indentation based on their hierarchy level.
1. Data Structure (employees array) The employees array represents a hierarchical structure, where each employee can have subordinates. Each employee object contains:
id: Unique identifier for the employee. name: Employee's name. position: The employee's job title. email: Employee's email address. subordinates: An array of subordinates (children), each following the same structure.
const employees = [ { id: 1, name: 'Hồ Bích Thảo', position: 'CEO', email: 'thaohb@example.com', subordinates: [ { id: 2, name: 'Nguyễn Văn A', position: 'Manager', email: 'vana@example.com', subordinates: [ { id: 3, name: 'Trần Văn B', position: 'Lead', email: 'vanb@example.com', subordinates: [ { id: 4, name: 'Lê Thị C', position: 'Developer', email: 'thic@example.com', subordinates: [], }, ], }, ], }, ], },
];
2. renderSubordinateRows function
This is a recursive function that flattens the hierarchy of subordinates. It goes through each subordinate and creates an entry in the table, adding a level to represent their position in the hierarchy (how deeply nested they are). The function uses recursion to dive into each subordinate's subordinates array, flattening the data structure.
Here’s how renderSubordinateRows works:
It processes the current subordinate by returning an object with details (name, position, email), along with the level (for indentation). For each subordinate, it calls itself recursively to process any deeper subordinates.
const renderSubordinateRows = (subordinates, level = 1) => { return subordinates.flatMap((subordinate) => [ { key: subordinate.id, // Unique key for each row name: subordinate.name, position: subordinate.position, email: subordinate.email, level: level, // Indentation level }, ...renderSubordinateRows(subordinate.subordinates, level + 1), // Recurse for subordinates ]);
};
3. The Main EmployeeTable Component This component renders the table of employees. There are two major parts to this:
Top-Level Employees (mainData):
Only the top-level employees are shown in the main table (dataSource={mainData}). These are the employees who do not have a parent (i.e., they are the root of the hierarchy). The level property is set to 0 for these employees, as they are the first level. Expandable Rows:
The expandable prop of the Ant Design Table component is used to make each row expandable. When a row (parent employee) is clicked, it will show a sub-table with the subordinates. The expandedRowRender function renders a nested table with each subordinate as its own row, and the subordinates are indented based on their level in the hierarchy. The nested table uses the renderSubordinateRows function to convert subordinates into rows. Here’s the main part of the component that deals with rendering:
<Table dataSource={mainData} // Top-level employees columns={columns} rowKey="id" pagination={false} expandable={{ expandedRowRender: (record) => { const subordinatesData = renderSubordinateRows(record.subordinates); // Process subordinates return ( <Table columns={columns} dataSource={subordinatesData} // Render subordinates as separate rows pagination={false} showHeader={false} // Hide the header for sub-table rowKey="key" // Unique row key for subordinates /> ); }, rowExpandable: (record) => record.subordinates.length > 0, // Only expand rows that have subordinates }}
/>
4. Columns and Indentation The columns array defines how to display each column of the table: Employee Name: The employee’s name is rendered with indentation based on the level property. The deeper the level (child of a child), the more indented the name will be. Position and Email: These columns display the respective details of the employee. The render function inside the Employee Name column uses the level property to control indentation:
{ title: 'Employee Name', dataIndex: 'name', key: 'name', render: (text, record) => ( <div style={{ paddingLeft: record.level * 20 }}> {text} </div> ),
}
This adds a paddingLeft to each employee’s name based on their level in the hierarchy, creating a visual indent.
5. Expandable Subordinates When the parent row is expanded, the expandedRowRender function is called, which in turn renders a nested table. This nested table contains rows for each subordinate, properly indented, and the showHeader={false} prop hides the header of the nested table (to maintain a clean look).
6. Result Top-Level Employees: These are rendered in the main table rows. Subordinates: When a parent employee is expanded, a sub-table shows the subordinates as separate rows. Each subordinate is indented based on its level in the hierarchy. No Duplicate Employees: Each employee appears only once in the table—top-level employees appear in the main table, and their subordinates are only shown in the expandable row. Conclusion This solution efficiently flattens the hierarchical structure into a table format, displaying each employee and their subordinates while preserving the hierarchy visually with indentation. It also uses Ant Design's expandable rows to display subordinates only when needed, making it user-friendly and efficient in terms of UI rendering.
Conclusion This solution efficiently flattens the hierarchical structure into a table format, displaying each employee and their subordinates while preserving the hierarchy visually with indentation. It also uses Ant Design's expandable rows to display subordinates only when needed, making it user-friendly and efficient in terms of UI rendering.