Constructing Dynamic Column Layouts in jQuery EasyUI Datagrid
In enterprise applications, user interfaces often require flexible layouts that adapt based on specific business contexts rather than remaining static. A common scenario involves configuring a DataGrid’s visible columns dynamically based on backend settings. This approach ensures that sensitive or irrelevant fields are hidden from users unless specific conditions are met.
The following implementation demonstrates how to fetch column configuration definitions via an API endpoint, merge them with a static base schema, and render the resulting layout efficiently. Key improvements over traditional methods include avoiding synchronous AJAX requests and utilizing modern JavaScript patterns for clearer execution flow.
Fetching Configuration Schema
Instead of hardcoding column structures, we define a dedicated service function responsible for retrieving the necessary metadata. This function accepts context parameters and returns a list of configuration objects compatible with the Datagrid plugin.
// Retrieve dynamic column definitions from the server
function fetchColumnConfiguration(params) {
const configEndpoint = '/api/CVRM/DynamicColumns';
return new Promise((resolve, reject) => {
$.ajax({
url: `${configEndpoint}?id=${params.masterId}"eType=${params.type}&bizType=${params.businessCategory}`,
method: 'GET',
dataType: 'json'
})
.done((response) => {
const formattedColumns = [];
if (Array.isArray(response) && response.length > 0) {
response.forEach((colDefinition, index) => {
// Map API response to grid column properties
formattedColumns.push({
field: `ID_${colDefinition.ColumnID}`,
title: colDefinition.ColumnTitle,
width: 100,
editor: {
type: 'numberbox'
}
});
});
}
resolve(formattedColumns);
})
.fail((jqXHR, status) => {
console.error('Failed to load column schema:', status);
reject(new Error(status));
});
});
}
Merging and Initializing the Grid
Once the dynamic schema is obtained, its concatenated with pre-defined columns required for core functionality, such as checkboxes or system tracking identifiers. This combined array is then passed to the initialization method. Note the separation of concerns between data fetching and UI rendering.
async function initializeShippingRateGrid(rowId) {
const containerSelector = '#gridTableFRT';
// 1. Define immutable structural columns
const baseLayout = [
{ checkbox: true },
{ field: 'FRTID', title: 'Identifier', align: 'center', width: 60, hidden: true },
{ field: 'PQID', title: 'Quote Reference', align: 'center', width: 60, hidden: true },
{ field: 'SourceLocID', title: 'Origin Location', align: 'center', width: 60, hidden: true, editor: { type: 'text' } },
{ field: 'DestLocID', title: 'Destination Location', align: 'center', width: 60, hidden: true, editor: { type: 'text' } }
];
try {
// 2. Retrieve dynamic columns asynchronously
const customColumns = await fetchColumnConfiguration({
masterId: 'MASTER_666666',
type: 'ProcurementPrice',
businessCategory: 'Vehicle'
});
// 3. Combine static and dynamic schemas
const finalColumns = [
...baseLayout,
{
field: 'SourceName',
title: 'Departure Zone',
width: 210,
align: 'center',
editor: {
type: 'combogrid',
options: {
panelWidth: 210,
mode: 'remote',
idField: 'AreaName',
textField: 'AreaName',
url: '/Master/Area/List',
columns: [[
{ field: 'AreaID', title: 'ID', width: 60, hidden: true },
{ field: 'AreaName', title: 'Zone Name', width: 200 }
]]
}
}
},
{
field: 'DestName',
title: 'Arrival Zone',
width: 210,
align: 'center',
editor: {
type: 'combogrid',
options: {
panelWidth: 210,
mode: 'remote',
idField: 'AreaName',
textField: 'AreaName',
url: '/Master/Area/List',
columns: [[
{ field: 'AreaID', title: 'ID', width: 60, hidden: true },
{ field: 'AreaName', title: 'Drop-off Zone', width: 200 }
]]
}
}
},
{ field: 'ContainerClass', title: 'Container Type', width: 110, align: 'center', editor: { type: 'combogrid', options: { url: '/Master/Container/List' } } },
{ field: 'GoodsClassification', title: 'Item Category', width: 100, align: 'center', editor: { type: 'combobox', options: { url: '/Sys/Code/Goods' } } },
{ field: 'MinCharge', title: 'Min Rate (CNY)', width: 120, align: 'left', editor: { type: 'numberbox', options: { precision: 2, required: true } } },
{ field: 'LeadTimeDays', title: 'Duration', width: 150, align: 'center', editor: { type: 'numberbox' } },
{ field: 'TotalDistanceKm', title: 'Mileage', width: 150, align: 'center', editor: { type: 'numberbox', options: { required: true } } },
{ field: 'HazardLevel', title: 'DG Class', width: 100, align: 'left', editor: { type: 'combobox', options: { url: '/Sys/Code/Hazards' } } },
{ field: 'ProductDescription', title: 'Description', width: 150, align: 'left', editor: { type: 'combogrid', options: { url: '/Master/Product/List' } } },
{ field: 'UNCode', title: 'UN Code', width: 120, align: 'left', editor: { type: 'text' } },
{ field: 'Remarks', title: 'Notes', width: 200, align: 'left', editor: { type: 'text' } }
];
// Append dynamic columns retrieved from API
if (customColumns && customColumns.length > 0) {
finalColumns.push(...customColumns);
}
// 4. Initialize the Datagrid with merged configuration
$(containerSelector).datagrid({
pageSize: 50,
pageList: [50, 100, 200],
singleSelect: false,
sortable: true,
url: '/api/CVRM/QuoteDetails',
queryParams: { refKey: rowId },
fitColumns: true,
columns: [finalColumns],
onLoadSuccess: function () {
$(containerSelector).datagrid('resize');
}
});
// Attach editing model if required
new com.editGridViewModel($(containerSelector));
// Adjust grid dimensions to viewport
$(containerSelector).datagrid('resize', {
width: $('.gridPanel').width(),
height: $(window).height() - $('.gridPanel').offset().top - 260
});
} catch (error) {
console.warn('Grid initialization halted due to schema error:', error);
}
}