Purpose#
Use a numeric main-form field to control the row count of a detail table automatically.
In this snippet:
field54450is the main-form control field.detail_2is detail table 2.- Empty or invalid control values do not change the detail table.
- A control value of
0deletes all rows in detail table 2. - If users manually add or delete rows in detail table 2, the snippet syncs the row count again after a short delay.
Code#
<script>
(function () {
'use strict';
var CONTROL_FIELD = 'field54450'; // Main-form control field
var DETAIL_MARK = 'detail_2'; // Detail table 2
var LOG_PREFIX = '[Detail 2 Row Count Control]';
var syncing = false;
function isEmpty(value) {
return value === undefined || value === null || String(value).trim() === '';
}
function parseTargetCount(value) {
if (isEmpty(value)) {
return null; // Empty values do not control the detail table
}
var count = parseInt(String(value).trim(), 10);
if (isNaN(count)) {
console.warn(LOG_PREFIX, 'Control field is not a valid number, skipped:', value);
return null;
}
// Control from 0; negative values are treated as 0
return Math.max(0, count);
}
function getDetailRowIndexes() {
var rowIndexStr = WfForm.getDetailAllRowIndexStr(DETAIL_MARK) || '';
if (rowIndexStr === '') {
return [];
}
return rowIndexStr.split(',').filter(function (rowIndex) {
return rowIndex !== '';
});
}
function syncDetailRows(triggerSource) {
if (syncing) {
return;
}
var controlValue = WfForm.getFieldValue(CONTROL_FIELD);
var targetCount = parseTargetCount(controlValue);
if (targetCount === null) {
console.log(LOG_PREFIX, triggerSource + ', empty or invalid value, row count not controlled', controlValue);
return;
}
syncing = true;
try {
var rowIndexes = getDetailRowIndexes();
var currentCount = rowIndexes.length;
console.log(LOG_PREFIX, triggerSource + ', start syncing', {
controlValue: controlValue,
targetCount: targetCount,
currentCount: currentCount,
rowIndexes: rowIndexes
});
if (currentCount < targetCount) {
for (var i = 0; i < targetCount - currentCount; i += 1) {
WfForm.addDetailRow(DETAIL_MARK, {});
}
}
if (currentCount > targetCount) {
var deleteCount = currentCount - targetCount;
var deleteRows = rowIndexes.slice(targetCount).reverse();
for (var j = 0; j < deleteCount; j += 1) {
WfForm.delDetailRow(DETAIL_MARK, deleteRows[j]);
}
}
console.log(LOG_PREFIX, 'Sync finished, target row count: ' + targetCount);
} catch (error) {
console.error(LOG_PREFIX, 'Failed to sync detail 2 row count', error);
} finally {
window.setTimeout(function () {
syncing = false;
}, 300);
}
}
jQuery(document).ready(function () {
// Run once 1 second after page load
window.setTimeout(function () {
syncDetailRows('1 second after page load');
}, 1000);
// Sync when the main-form field changes
WfForm.bindFieldChangeEvent(CONTROL_FIELD, function () {
syncDetailRows('field54450 changed');
});
// Resync after manual row changes so the detail table stays aligned with the control field
if (WfForm.ACTION_ADDROW && WfForm.registerAction) {
WfForm.registerAction(WfForm.ACTION_ADDROW + '2', function () {
window.setTimeout(function () {
syncDetailRows('detail 2 manually added row');
}, 300);
});
}
if (WfForm.ACTION_DELROW && WfForm.registerAction) {
WfForm.registerAction(WfForm.ACTION_DELROW + '2', function () {
window.setTimeout(function () {
syncDetailRows('detail 2 manually deleted row');
}, 300);
});
}
});
})();
</script>What to Change#
- To change the main-form control field, update
CONTROL_FIELD. - To change the target detail table, update
DETAIL_MARK. - If the target is not detail table 2, also change the number in
ACTION_ADDROW + '2'andACTION_DELROW + '2'to the corresponding detail-table index. - If a control value of
0should not clear the detail table, changeparseTargetCountso values below1return1.
