Hey ,
One possible approach is to use a custom widget or a dynamically generated layout instead of Datagrid2, since Datagrid2 requires predefined columns. You can retrieve the data for the selected File_Id, group the records based on the required structure, and dynamically generate the column headers from the distinct Field_Name values. The corresponding Field_Value can then be displayed under each column. This would require some custom implementation, as the standard Mendix widgets do not directly support dynamic columns based on the data.
Regards
Reemali
Hi Alexia,
This is a case where I would avoid trying to make the Data Grid 2 columns dynamic. Data Grid 2 requires the columns to be defined in the page configuration. Although columns can be conditionally shown/hidden using the Visible expression, it does not create new columns dynamically at runtime.
In your case, the data is essentially an EAV/key-value structure:
File_Id | Field_Name | Field_Value ---------------------------------- 1 | Order No. | 12345 1 | Item No. | 00000 1 | Customer | ABC 2 | Order No. | 67890 2 | Item No. | 11111 2 | Supplier | XYZ
You want to pivot that into:
Order No. | Item No. | Customer | Supplier ------------------------------------------ 12345 | 00000 | ABC | 67890 | 11111 | | XYZ
The problem is that Order No., Item No., Customer, Supplier, etc. are not known at design time.
If there is actually a finite set of possible fields, I would model them as attributes on a view/helper entity and configure those as normal Data Grid 2 columns.
For example:
DynamicFileRow ---------------- OrderNo ItemNo Customer Supplier
Then Data Grid 2 works normally.
You can also use the Visible expression to hide columns that are not applicable for the selected file. This is supported by Data Grid 2.
If a file can contain completely different field names and there is no fixed maximum set, I would not use Data Grid 2 for the final presentation.
Instead, I would use a custom table/grid component where the headers and cells are generated from the runtime data.
The structure would be:
Selected File
↓
Retrieve Field_Name / Field_Value records
↓
Build distinct Field_Name list
↓
Build rows grouped by the business record
↓
Custom dynamic table
↓
Dynamic headers + dynamic cell values
For example, the UI component receives something like:
Columns: [ "Order No.", "Item No.", "Customer", "Supplier" ] Rows: [ ["12345", "00000", "ABC", ""], ["67890", "11111", "", "XYZ"] ]
and renders the table dynamically.
I would keep the original Field_Name / Field_Value / File_Id structure in the Mendix domain model rather than creating database attributes dynamically. Mendix attributes and Data Grid 2 columns are design-time model elements, so creating a new database attribute/column for every new field is not a suitable runtime solution.
If you need the data to remain completely generic, another option is to create a view entity/OQL-based read model for the required transformation. View entities can expose dynamically queried data, but they still do not make Data Grid 2 columns dynamic; the grid columns themselves remain configured in the page.
So I would choose between these two approaches:
Known / controlled set of fields
↓
Helper/View entity
↓
Data Grid 2
or
Unknown fields at runtime
↓
Field_Name / Field_Value model
↓
Pivot/group the data
↓
Custom dynamic table widget
For your example, because each file can contain a different set of fields, I would go with the second approach rather than trying to work around Data Grid 2.
Hope this helps.
JavaScript Snippet
Instead of forcing Data Grid 2 to have dynamic columns, use a Pluggable Widget with JavaScript (or an HTML/JavaScript snippet widget from the Marketplace).
Use a microflow to retrieve the rows (Name, Value, Id), store them on a dataview.
Then use a snipplet that dynamically builds HTML <table>, <thead> and <tbody> elements at runtime.
Example JSON:
[
{ "Order": "12345", "Item": "00000", "Name": "ABC" },
{ "Order": "67890", "Item": "11111", "Name": "XYZ" }
]
Javascript code
contextObject.get('YourModuleName.HelperEntity.DynamicJSONDataView'))
const rawData = contextObject ? contextObject.get("DynamicJSONDataView") : "[]";
let rows = [];
try {
rows = JSON.parse(rawData);
} catch (e) {
console.error("Invalid JSON data:", e);
}
const container = document.getElementById("dynamic-table-container");
if (!container) return;
if (!rows || rows.length === 0) {
container.innerHTML = "<p>No data available for this file.</p>";
return;
}
const columns = [...new Set(rows.flatMap(row => Object.keys(row)))];
let html = '<div class="table-responsive"><table class="table table-striped table-bordered"><thead><tr>';
columns.forEach(col => {
html += `<th>${parseHtml(col)}</th>`;
});
html += '</tr></thead><tbody>';
rows.forEach(row => {
html += '<tr>';
columns.forEach(col => {
html += `<td>${parseHtml(row[col] || '')}</td>`;
});
html += '</tr>';
});
html += '</tbody></table></div>';
container.innerHTML = html;
function parseHtml(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
Put this at the end of the snipplet <div id="dynamic-table-container"></div>
Regards!