Using Table
Dynamically Modifying Table Columns
The Table API provides the updateComponentProps method, which can be used to modify any table properties, including the column definitions (columnDefs).
For example, after the table is rendered, clicking the "Add Column" button dynamically adds a new column 12/2/2024 to the table.

The configuration in the designer is as follows:

The JavaScript code for this example is as follows:
formApi.registerMethod('changeColumnDefs', () => {
const tableApi = formApi.getFieldApi('45cfdeaa-table');
const { columnDefs } = tableApi.getComponentProps();
const newColDef = {
colId: new Date().toLocaleDateString(),
field: new Date().toLocaleDateString(),
headerName: new Date().toLocaleDateString(),
};
const newColumnDefs = [...columnDefs, newColDef];
tableApi.updateComponentProps({
columnDefs: newColumnDefs,
});
});
Complex Table Column Definitions
Using the updateComponentProps method mentioned above, you can dynamically modify the column definitions to achieve complex table column definitions.

Complex table column definitions must be implemented using JavaScript code in the "JS Code" section. For more information, refer to the official ag-Grid documentation: https://www.ag-grid.com/javascript-data-grid/component-cell-renderer/.
The JavaScript code for this example is as follows:
formApi.on('ready', function () {
const tableApi = formApi.getFieldApi('test-table');
const { columnDefs } = tableApi.getComponentProps();
const additionalColumnDefs = [
{
headerName: 'Plain String',
field: 'code',
colId: 'code',
},
{
headerName: 'Two Lines Display',
field: 'name',
cellRenderer: BasicInfoRender,
},
{
headerName: 'Render Color',
field: 'colorSize',
cellRenderer: ColorSizeRender,
},
{
headerName: 'Text with Link',
field: 'order',
cellRenderer: OrderRender,
},
{
headerName: 'Table',
field: 'countDetail',
cellRenderer: CountDetailRender,
},
{
headerName: 'Open Modal',
field: 'openModal',
cellRenderer: OpenModalRender,
},
{
headerName: 'Header Spanning Columns',
colId: 'arrange',
field: 'arrange',
children: [
{
colId: 'color',
field: 'color',
headerName: 'Column One',
},
{
colId: 'number',
field: 'number',
headerName: 'Column Two',
},
],
},
];
const newColumnDefs = [...columnDefs, ...additionalColumnDefs];
tableApi.updateComponentProps({
rowHeight: 220,
columnDefs: newColumnDefs,
});
});
class BasicInfoRender {
eGui;
// Optional: Params for rendering. The same params that are passed to the cellRenderer function.
init(params) {
const { code, name } = params.data;
this.eGui = document.createElement('div');
this.eGui.innerHTML = `<div >
<span>Code: </span>
<span>${code}</span>
</div>
<div >
<span>Name: </span>
<span>${name}</span>
</div>
</div>`;
}
// Required: Return the DOM element of the component, this is what the grid puts into the cell
getGui() {
return this.eGui;
}
// Required: Get the cell to refresh.
refresh(params) {
return false;
}
}
class ColorSizeRender {
eGui;
generateTag(name) {
return `<span class="table-cell-tag">${name}</span>`;
}
// Optional: Params for rendering. The same params that are passed to the cellRenderer function.
init(params) {
const { color, size } = params.data;
this.eGui = document.createElement('div');
this.eGui.innerHTML = `<div ><div >
<span>Color: </span>
<span>${color
.map((item) => {
return this.generateTag(item);
})
.join(' ')}</span>
</div>
</div>`;
}
// Required: Return the DOM element of the component, this is what the grid puts into the cell
getGui() {
return this.eGui;
}
// Required: Get the cell to refresh.
refresh(params) {
return false;
}
}
class OrderRender {
eGui;
generateTag(name) {
return `<span class="table-cell-tag">${name}</span>`;
}
// Optional: Params for rendering. The same params that are passed to the cellRenderer function.
init(params) {
const { count, orderId } = params.data;
this.eGui = document.createElement('div');
this.eGui.innerHTML = `<div ><div >
<span>Count: </span>
<span>${count}</span>
</div>
<a href="/order${orderId}/view" class="table-cell-mock-button">View</a>
</div>`;
}
// Required: Return the DOM element of the component, this is what the grid puts into the cell
getGui() {
return this.eGui;
}
// Required: Get the cell to refresh.
refresh(params) {
return false;
}
}
class CountDetailRender {
eGui;
// Optional: Params for rendering. The same params that are passed to the cellRenderer function.
init(params) {
const { count, orderId } = params.data;
this.eGui = document.createElement('div');
this.eGui.innerHTML = `<table class="table-cell-table">
<tr>
<td>Color\Size</td>
<td>S</td>
<td>M</td>
<td>Total</td>
</tr>
<tr>
<td>Black</td>
<td>100</td>
<td>200</td>
<td>300</td>
</tr>
<tr>
<td>White</td>
<td>300</td>
<td>400</td>
<td>700</td>
</tr>
<tr>
<td>Total</td>
<td>400</td>
<td>600</td>
<td>1000</td>
</tr>
</table>`;
}
// Required: Return the DOM element of the component, this is what the grid puts into the cell
getGui() {
return this.eGui;
}
// Required: Get the cell to refresh.
refresh(params) {
return false;
}
}
class OpenModalRender {
eGui;
eButton;
eventListener;
generateTag(name) {
return `<span class="table-cell-tag">${name}</span>`;
}
// Optional: Params for rendering. The same params that are passed to the cellRenderer function.
init(params) {
const { count, orderId } = params.data;
this.eGui = document.createElement('div');
this.eButton = document.createElement('button');
this.eButton.className = 'btn-simple';
this.eButton.textContent = 'Edit';
this.eventListener = () => createModal();
this.eButton.addEventListener('click', this.eventListener);
this.eGui.appendChild(this.eButton);
}
// Required: Return the DOM element of the component, this is what the grid puts into the cell
getGui() {
return this.eGui;
}
// Required: Get the cell to refresh.
refresh() {
return true;
}
destroy() {
if (this.eButton) {
this.eButton.removeEventListener('click', this.eventListener);
}
}
}
function createModal() {
// Check if a modal already exists to avoid duplication
const existingModal = document.getElementById('modal');
if (existingModal) {
existingModal.style.display = 'block'; // If it exists, just show it
return;
}
// Create the HTML for the modal
const modalHTML = `
<div id="modal" class="modal">
<div class="modal-content">
<span class="close-btn">×</span>
<h2>Modal Title</h2>
<p>This is a modal with an embedded <iframe>.</p>
<iframe width="100%" height="300px" frameborder="0"></iframe>
</div>
</div>
`;
// Append the modal to the body
document.body.insertAdjacentHTML('beforeend', modalHTML);
// Get the newly created elements
const modal = document.getElementById('modal');
const closeBtn = modal.querySelector('.close-btn');
const iframe = modal.querySelector('iframe');
// Show the modal
modal.style.display = 'block';
// Set the iframe src (lazy loading)
iframe.src = 'https://baidu.com'; // This can be modified to another link
// Close the modal
closeBtn.addEventListener('click', () => {
modal.remove(); // Remove the modal
});
// Close the modal when clicking outside
modal.addEventListener('click', (event) => {
if (event.target === modal) {
modal.remove(); // Remove the modal
}
});
}
The CSS code in the "Frontend Configuration" for this example is as follows:
.table-cell-tag {
display: inline-block;
border: 1px solid grey;
padding: 4px;
line-height: 1.32;
background: #E6F4FF;
color: #1677FF;
}
.table-cell-mock-button {
display: inline-block;
padding: 5px;
line-height: 1.32;
background-color: #1677FF;
color: white;
text-decoration: none;
border-radius: 5px;
}
.table-cell-table {
border-collapse: collapse;
}
.table-cell-table td {
border: 1px solid;
width: 100px;
}
/* Modal container */
.modal {
display: none; /* Hidden by default */
position: fixed;
z-index: 1000; /* Ensure it is on top */
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.4); /* Semi-transparent background */
}
/* Modal content */
.modal-content {
background-color: #fefefe;
margin: 15% auto;
padding: 20px;
border: 1px solid #888;
width: 80%;
max-width: 600px; /* Adjust width to fit iframe */
position: relative;
}
/* Close button */
.close-btn {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
cursor: pointer;
}
.close-btn:hover {
color: #000;
}
/* iframe styling */
iframe {
border: none; /* Remove iframe border */
margin-top: 10px; /* Add some spacing */
width: 100%;
height: 300px;
}