JDitoRecordContainer
Unlike a dbRecordContainer, a JDitoRecordContainer uses JDito code as its data source. That code may still load data from a database, but the effective source is the result returned by contentProcess.
The result of contentProcess must be a nested array whose order matches the configured recordFieldMappings.
When to use it
Use a JDitoRecordContainer when the standard database mapping is not sufficient, for example when:
- the data must be assembled dynamically,
- multiple sources must be combined manually,
- the result does not map cleanly to a standard table-based structure, or
- custom paging, filtering, or sorting logic is required.
The main benefit of JDitoRecordContainer is flexibility. The data can be loaded from a database, a web service, or any other source that is accessible through JDito.
You must handle sorting, paging, and filtering manually.
Creating a JDitoRecordContainer
To establish a JDitoRecordContainer, proceed as follows:
- Open the respective Entity in the Navigator window.
- Right-click it and choose New RecordContainer from the context menu.
- In the dialog, choose JDitoRecordContainer as the type and enter a name. The convention is to name it
jdito. - Select the new RecordContainer under RecordContainers and edit its properties.
- In
recordFieldMappings, add the EntityFields that are filled by theJDitoRecordContainer. - In
contentProcess, implement the core logic of theJDitoRecordContainer. The process result must always be a nested array that acts as the data source.
The order of the fields in recordFieldMappings must match the order of the data in the array built in contentProcess. An EntityField named UID, spelled exactly like this and with content type TEXT, must always be present and included in the field mapping.
Example from xRM
In Turnover_entity in module revenue, contentProcess fills a variable chartData using:
// EntityFields: UID, PARENT, CATEGORY, X, Y
chartData.push([key, countDataSet.parent, countDataSet.category, countDataSet.x, countDataSet.count]);
Finally, return the array:
result.object(chartData);
The structured data is displayed in charts within a GroupLayout, for example in Sales > Opportunity > MainView > tab Forecast.
Important properties
| Property | Description |
|---|---|
jDitoRecordAlias | Defines the default alias used in database access methods. It is often set to Data_alias. |
recordFieldMappings | Maps EntityFields to the array values of contentProcess. The order must match exactly. |
isPageable | Enables paging. Access $local.startrow and $local.pagesize in contentProcess. |
isFilterable | Enables filtering. Access $local.filter, which contains a map of field, operator, and value. |
isRequireContainerFiltering | Enables server-side filtering for performance. |
isSortable | Enables sorting. Access $local.order, which contains a map of field and direction. |
contentProcess | Provides the data and returns a nested array. |
rowCountProcess | Returns the row count. If paging is enabled or permission-based filtering is used, this process is mandatory. It must return a numeric count and use the same effective filter logic as contentProcess. |
hasDependentRecords | Supports hierarchical structures, for example trees. contentProcess is re-executed after deletion. |
onInsert | Handles new records by using $local.rowdata. |
onUpdate | Updates changed data fields by using $local.changed and $local.rowdata. |
onDelete | Deletes a record by using $local.uid. |
contentProcess
The contentProcess is responsible for providing the data for the RecordContainer. The record data should be returned as a nested array, for example:
let data = [
["UID1", "VALUE1.1", "VALUE2.1", "VALUE3.1"],
["UID2", "VALUE1.2", "VALUE2.2", "VALUE3.2"],
["UID3", "VALUE1.3", "VALUE2.3", "VALUE3.3"]
];
result.object(data);
Available JDito variables
| Variable | Description |
|---|---|
$local.idvalues | UIDs of the requested records as an array. Is null when all records should be loaded. |
$local.idvaluesExcluded | UIDs of the records that should be excluded from the result. Can be null. |
$local.order | The requested sorting order as an object, with the field names as keys and the sorting direction as values. Can be null. |
$local.filter | Filter object that contains multiple sub-filters defining what records should be loaded. |
$local.userfilter | The externally defined filter set by the requestor. |
$local.filters | Combined search condition object that contains all relevant filter conditions. |
$local.grouped | The name of the grouping field for tree data-structures. Only available when a grouping is defined. |
$local.yAxis | The field which defines the Y axis of a chart. |
$local.fieldhints | Contains the names of the fields that should be loaded. Can be used for performance optimizations. |
The following variables are exclusive to the contentProcess and not available in the rowCountProcess: $local.order, $local.userfilter, $local.fieldhints
Saving and deleting records
If the Entity should support the creation, modification or deletion of records, you must implement these operations yourself when using a JDitoRecordContainer. These actions can be implemented as JDito processes.
Avoid referencing EntityFields in RecordContainer processes directly by using $field variables. You should use the RecordFields defined by the recordFieldMapping instead, which can be accessed through the variables $local.rowdata and $local.uid.
onInsert
The onInsert process handles the creation of new records. This example does that by inserting a row into the database:
let rowdata = vars.get("$local.rowdata");
let fieldValues = {
"UID": rowdata["UID.value"],
"SUBJECT": rowdata["SUBJECT.value"],
"DESCRIPTION": rowdata["DESCRIPTION.value"],
"ENTRYDATE": rowdata["ENTRYDATE.value"]
};
new SqlBuilder().insertFields(fieldValues, "YOURTABLE");
onUpdate
The onUpdate process handles the modification of existing records. The following example updates values in the database:
let changedFields = vars.get("$local.changed");
let rowData = vars.get("$local.rowdata");
let fieldValues = {};
changedFields.forEach(fieldName =>
{
// removes the ".value"/".displayValue" postfix
let dbFieldName = fieldName.split(".")[0];
fieldValues[dbFieldName] = rowData[fieldName];
});
newWhere("YOURTABLE.YOURTABLEID", vars.get("$local.uid"))
.updateFields(fieldValues);
onDelete
The onDelete process handles the deletion of a record, for example by removing a row in the database:
newWhere("YOURTABLE.YOURTABLEID", vars.get("$local.uid"))
.deleteData();
A step-by-step example is available in Examples.
Filtering a JDitoRecordContainer
The previous example shows basic filter integration. For more complex cases, use helper functions from RecordFilter_lib, RecordFilterUtils_lib, and FilterSqlTranslator_lib, to build SQL conditions or to implement manual filtering.
You can find practical implementations of advanced filtering in JDitoRecordContainers from contexts such as Attribute, Manager, or Workflow.
If permission-based filtering is used, rowCountProcess is mandatory and must apply the same effective filtering as contentProcess.
Permissions in a JDitoRecordContainer
Permission-based filtering in a JDitoRecordContainer is evaluated through
$local.filter.permissions. If this mechanism is used in contentProcess,
rowCountProcess must also be implemented. This is not optional. Both
processes must evaluate the same effective filter logic so that the visible
rows and the reported row count stay consistent.
In practice, this means that contentProcess and rowCountProcess must both
consider the complete filter state, including $local.filter.filter and
$local.filter.permissions. In addition, rowCountProcess must always return
a numeric value and must never return null.
If these requirements are not met, the container may still display rows
correctly at first, but follow-up behavior can become inconsistent. Typical
symptoms include missing or incorrect row counts, failing permission checks, or
errors during editing even though contentProcess itself returns valid data.