Column Chooser in React Treegrid component
1 Oct 202524 minutes to read
Column Chooser Template in Syncfusion React TreeGrid
The Column Chooser Template feature enables full customization of the column chooser header, content, and footer to manage column visibility. To enable the column chooser, set showColumnChooser to true and include ColumnChooser in the toolbar property.
To implement a custom column chooser template in the TreeGrid, use the following properties:
- columnChooserSettings.headerTemplate: Defines the header template.
- columnChooserSettings.template: Defines the content template.
- columnChooserSettings.footerTemplate: Defines the footer template.
In this example, the Syncfusion TreeView component is rendered inside the column chooser. To use the TreeView component, install the Syncfusion TreeView package as described in the TreeView getting started documentation. The columnChooserSettings.template
property renders the TreeView with checkboxes. Checkbox selection is handled using the nodeClicked and keyPress events, organizing columns into Order Details, Shipping Details, and Delivery Status.
The column chooser footer is customized using columnChooserSettings.footerTemplate
, replacing default buttons with customized Apply and Close buttons. The Apply button updates column visibility based on the selected nodes, and the Close button dismisses the dialog via the onClick
event. The header is customized using columnChooserSettings.headerTemplate
to include a title and an icon.
import * as React from 'react';
import { createRoot } from 'react-dom/client';
import { TreeGridComponent, ColumnsDirective, ColumnDirective, Page, Toolbar, ColumnChooser, ToolbarItems, Inject } from '@syncfusion/ej2-react-treegrid';
import { stackedData } from './data';
import { TreeView, TreeViewComponent } from '@syncfusion/ej2-react-navigations';
import { ButtonComponent } from '@syncfusion/ej2-react-buttons';
function App() {
const toolbar = ['ColumnChooser'];
const columnChooserSettings = { template: Template, headerTemplate: HeaderTemplate, footerTemplate: FooterTemplate }
function HeaderTemplate() {
return (
<div>
<span id="column-chooser-text" style=>Column Options</span>
</div>
);
}
// Render TreeView in the column chooser's Content
function Template(props) {
const parentNodes = [
{ id: 1, name: 'Order Details', hasChild: true, expanded: true },
{ id: 2, name: 'Shipment Details', hasChild: true, expanded: true },
{ id: 3, name: 'Price Details', hasChild: true, expanded: true },
];
let treeData = [];
if (props.columns && props.columns.length) {
treeData = props.columns.map((column) => {
let parentId;
switch (column.field) {
case 'orderID':
case 'orderName':
case 'orderDate':
parentId = 1;
break;
case 'shipMentCategory':
case 'shippedDate':
case 'units':
parentId = 2;
break;
case 'unitPrice':
case 'price':
parentId = 3;
break;
default:
break;
}
return {
id: column.uid,
name: column.headerText,
pid: parentId,
isChecked: column.visible
};
});
const uniquePids = [];
treeData.forEach((item) => {
if (!uniquePids.includes(item.pid)) {
uniquePids.push(item.pid);
}
});
const filteredParents = parentNodes.filter((parent) => uniquePids.includes(parent.id));
treeData.push(...filteredParents);
} else {
treeData = [];
}
const fields = { dataSource: treeData, id: 'id', parentID: 'pid', text: 'name', hasChildren: 'hasChild' };
return (
<div>
{props.columns && props.columns.length ? (<TreeViewComponent fields={fields} cssClass="no-border" showCheckBox={true} nodeClicked={nodeCheck} keyPress={nodeCheck} ref={(treeview ) => { treeObj = treeview; }} />) : (<div className="no-record-text">No Matches Found</div>)}
</div>
);
}
function FooterTemplate() {
return (
<div id="columnChooserFooter">
<ButtonComponent onClick={columnChooserSubmit}>Apply</ButtonComponent>
<ButtonComponent onClick={columnChooserClose}>Close</ButtonComponent>
</div>
);
}
// Handle checking/unchecking nodes in the TreeView (column chooser)
function nodeCheck(args) {
let checkedNode = [args.node];
if (args.event.target.classList.contains('e-fullrow') || args.event.key == "Enter") {
let getNodeDetails = treeObj.getNode(args.node);
if (getNodeDetails.isChecked == 'true') {
treeObj.uncheckAll(checkedNode);
} else {
treeObj.checkAll(checkedNode);
}
}
}
function columnChooserClose() {
(treegridInstance.grid.columnChooserModule).hideDialog();
}
// Apply the column chooser selection
function columnChooserSubmit() {
const checkedElements = [];
const uncheckedElements = [];
var showColumns = treegridInstance.getVisibleColumns().filter(function (column) { return (column.showInColumnChooser === true); });
showColumns = showColumns.map(function (col) { return col.headerText; });
const treeItems = document.querySelectorAll('.e-list-item');
treeItems.forEach(item => {
const itemDetails = treeObj.getNode(item);
if (!itemDetails.hasChildren) {
if (item.getAttribute('aria-checked') === 'true') {
checkedElements.push(itemDetails.text);
} else {
uncheckedElements.push(itemDetails.text);
}
}
});
showColumns = showColumns.filter((col) => !uncheckedElements.includes(col));
checkedElements.forEach((item) => {
if (!showColumns.includes(item)) {
showColumns.push(item);
}
});
var columnsToUpdate = { visibleColumns: showColumns, hiddenColumns: uncheckedElements };
treegridInstance.grid.columnChooserModule.changeColumnVisibility(columnsToUpdate);
}
let treeObj;
let treegridInstance;
return (
<div className="control-pane">
<div className="control-section">
<TreeGridComponent
dataSource={stackedData}
id="TreeGrid"
ref={(treegrid ) => { treegridInstance = treegrid }}
treeColumnIndex={1}
childMapping="subtasks"
height="350"
allowPaging={true}
pageSettings=
showColumnChooser={true}
columnChooserSettings={columnChooserSettings}
toolbar={toolbar}
clipMode='EllipsisWithTooltip'
>
<ColumnsDirective>
<ColumnDirective
columns={[
{
field: "orderID",
headerText: "Order ID",
width: 90,
textAlign: "Right",
showInColumnChooser: false,
},
{
field: "orderName",
headerText: "Order Name",
width: 190,
textAlign: "Left",
},
{
field: "orderDate",
headerText: "Order Date",
width: 110,
textAlign: "Right",
format: "yMd",
},
]}
headerText="Order Details"
textAlign="Center"
></ColumnDirective>
<ColumnDirective
columns={[
{
field: "shipMentCategory",
headerText: "Shipment Category",
width: 150,
textAlign: "Left",
},
{
field: "shippedDate",
headerText: "Shipped Date",
width: 120,
textAlign: "Right",
format: "yMd",
},
{
field: "units",
headerText: "Units",
width: 80,
textAlign: "Right",
},
]}
headerText="Shipment Details"
textAlign="Center"
></ColumnDirective>
<ColumnDirective
columns={[
{
field: "unitPrice",
headerText: "Price per unit",
format: "C2",
type: "number",
textAlign: "Right",
width: 120,
},
{
field: "price",
headerText: "Total Price",
width: 115,
format: "C",
textAlign: "Right",
type: "number",
},
]}
headerText="Price Details"
textAlign="Center"
></ColumnDirective>
</ColumnsDirective>
<Inject services={[Page, Toolbar, ColumnChooser,]} />
</TreeGridComponent>
</div>
</div>
);
};
export default App;
import * as React from 'react';
import { createRoot } from 'react-dom/client';
import { TreeGridComponent, ColumnsDirective, ColumnDirective, Page, Toolbar, ColumnChooser, ToolbarItems, Inject } from '@syncfusion/ej2-react-treegrid';
import { stackedData } from './data';
import { TreeView, TreeViewComponent } from '@syncfusion/ej2-react-navigations';
import { ButtonComponent } from '@syncfusion/ej2-react-buttons';
function App() {
const toolbar: any = ['ColumnChooser'];
const columnChooserSettings: any = { template: Template, headerTemplate: HeaderTemplate, footerTemplate: FooterTemplate }
function HeaderTemplate(): any {
return (
<div>
<span id="column-chooser-text" style=>Column Options</span>
</div>
);
}
// Render TreeView in the column chooser's Content
function Template(props: any): any {
const parentNodes = [
{ id: 1, name: 'Order Details', hasChild: true, expanded: true },
{ id: 2, name: 'Shipment Details', hasChild: true, expanded: true },
{ id: 3, name: 'Price Details', hasChild: true, expanded: true },
];
let treeData: { [key: string]: Object }[] = [];
if (props.columns && props.columns.length) {
treeData = props.columns.map((column: any) => {
let parentId: any;
switch (column.field) {
case 'orderID':
case 'orderName':
case 'orderDate':
parentId = 1;
break;
case 'shipMentCategory':
case 'shippedDate':
case 'units':
parentId = 2;
break;
case 'unitPrice':
case 'price':
parentId = 3;
break;
default:
break;
}
return {
id: column.uid,
name: column.headerText,
pid: parentId,
isChecked: column.visible
};
});
const uniquePids: string[] = [];
treeData.forEach((item: any) => {
if (!uniquePids.includes(item.pid)) {
uniquePids.push(item.pid);
}
});
const filteredParents = parentNodes.filter((parent: any) => uniquePids.includes(parent.id));
treeData.push(...filteredParents);
} else {
treeData = [];
}
const fields = { dataSource: treeData, id: 'id', parentID: 'pid', text: 'name', hasChildren: 'hasChild' };
return (
<div>
{props.columns && props.columns.length ? (<TreeViewComponent fields={fields} cssClass="no-border" showCheckBox={true} nodeClicked={nodeCheck} keyPress={nodeCheck} ref={(treeview : any) => { treeObj = treeview; }} />) : (<div className="no-record-text">No Matches Found</div>)}
</div>
);
}
function FooterTemplate(): any {
return (
<div id="columnChooserFooter">
<ButtonComponent onClick={columnChooserSubmit}>Apply</ButtonComponent>
<ButtonComponent onClick={columnChooserClose}>Close</ButtonComponent>
</div>
);
}
// Handle checking/unchecking nodes in the TreeView (column chooser)
function nodeCheck(args: any): void {
let checkedNode: any = [args.node];
if (args.event.target.classList.contains('e-fullrow') || args.event.key == "Enter") {
let getNodeDetails: any = treeObj.getNode(args.node);
if (getNodeDetails.isChecked == 'true') {
treeObj.uncheckAll(checkedNode);
} else {
treeObj.checkAll(checkedNode);
}
}
}
function columnChooserClose() {
(treegridInstance.grid.columnChooserModule as any).hideDialog();
}
// Apply the column chooser selection
function columnChooserSubmit() {
const checkedElements: any = [];
const uncheckedElements: any = [];
var showColumns: any = treegridInstance.getVisibleColumns().filter(function (column: any) { return (column.showInColumnChooser === true); });
showColumns = showColumns.map(function (col: any) { return col.headerText; });
const treeItems = document.querySelectorAll('.e-list-item');
treeItems.forEach(item => {
const itemDetails = treeObj.getNode(item);
if (!itemDetails.hasChildren) {
if (item.getAttribute('aria-checked') === 'true') {
checkedElements.push(itemDetails.text);
} else {
uncheckedElements.push(itemDetails.text);
}
}
});
showColumns = showColumns.filter((col: any) => !uncheckedElements.includes(col));
checkedElements.forEach((item :any) => {
if (!showColumns.includes(item)) {
showColumns.push(item);
}
});
var columnsToUpdate: any = { visibleColumns: showColumns, hiddenColumns: uncheckedElements };
treegridInstance.grid.columnChooserModule.changeColumnVisibility(columnsToUpdate);
}
let treeObj: TreeView;
let treegridInstance: TreeGridComponent;
return (
<div className="control-pane">
<div className="control-section">
<TreeGridComponent
dataSource={stackedData}
id="TreeGrid"
ref={(treegrid :any) => { treegridInstance = treegrid }}
treeColumnIndex={1}
childMapping="subtasks"
height="350"
allowPaging={true}
pageSettings=
showColumnChooser={true}
columnChooserSettings={columnChooserSettings}
toolbar={toolbar}
clipMode='EllipsisWithTooltip'
>
<ColumnsDirective>
<ColumnDirective
columns={[
{
field: "orderID",
headerText: "Order ID",
width: 90,
textAlign: "Right",
showInColumnChooser: false,
},
{
field: "orderName",
headerText: "Order Name",
width: 190,
textAlign: "Left",
},
{
field: "orderDate",
headerText: "Order Date",
width: 110,
textAlign: "Right",
format: "yMd",
},
]}
headerText="Order Details"
textAlign="Center"
></ColumnDirective>
<ColumnDirective
columns={[
{
field: "shipMentCategory",
headerText: "Shipment Category",
width: 150,
textAlign: "Left",
},
{
field: "shippedDate",
headerText: "Shipped Date",
width: 120,
textAlign: "Right",
format: "yMd",
},
{
field: "units",
headerText: "Units",
width: 80,
textAlign: "Right",
},
]}
headerText="Shipment Details"
textAlign="Center"
></ColumnDirective>
<ColumnDirective
columns={[
{
field: "unitPrice",
headerText: "Price per unit",
format: "C2",
type: "number",
textAlign: "Right",
width: 120,
},
{
field: "price",
headerText: "Total Price",
width: 115,
format: "C",
textAlign: "Right",
type: "number",
},
]}
headerText="Price Details"
textAlign="Center"
></ColumnDirective>
</ColumnsDirective>
<Inject services={[Page, Toolbar, ColumnChooser,]} />
</TreeGridComponent>
</div>
</div>
);
};
export default App;