When users submit tabular data inside a Joget DX Form Grid (such as multi-line expense claims, asset transfers, or approver lists), users frequently enter duplicate items by accident.
While standard form validators check whether individual cells are empty or match a format, they cannot compare cells across different grid rows.
In this guide, we'll write a custom BeanShell Form Validator script attached to a Form Grid that detects duplicate column values across grid rows, identifies the exact row numbers where duplicates occur, and halts form submission with a clear error message.
How It Works
-
Row Iteration: The validator receives the submitted grid rows as a
FormRowSetcollection (rows). -
Frequency Map Tracking: Using a
HashMap<String, List<Integer>>, the script tracks each value along with a list of row indices where that value appears. -
Descriptive Error Reporting: If any value occurs more than once, an error message specifies the exact duplicate value and row locations (e.g.,
Duplicate Serial Number 'SN-9021' found at Rows: 2, 5).
The BeanShell Form Validator Script
In Joget Form Builder, edit your Form Grid element, set Validator to BeanShell Validator, and paste the script below:
import org.joget.apps.form.model.Element;
import org.joget.apps.form.model.FormData;
import org.joget.apps.form.model.FormRow;
import org.joget.apps.form.service.FormUtil;
import org.joget.commons.util.LogUtil;
import java.util.*;
public boolean validateGridDuplicates(Element element, FormData formData, FormRowSet rows) {
String elementId = FormUtil.getElementParameterName(element);
if (rows == null || rows.isEmpty()) {
return true; // No rows to validate
}
// Name of the grid column property to check for duplicates
String targetColumnProperty = "serial_number";
// Map: Key = Column Value, Value = List of 1-based Row Numbers
Map valueOccurrencesMap = new HashMap();
int currentRowNumber = 0;
for (Object oRow : rows) {
FormRow row = (FormRow) oRow;
currentRowNumber++;
String cellValue = row.getProperty(targetColumnProperty);
if (cellValue != null && !cellValue.trim().isEmpty()) {
cellValue = cellValue.trim();
if (!valueOccurrencesMap.containsKey(cellValue)) {
valueOccurrencesMap.put(cellValue, new ArrayList());
}
List rowList = (List) valueOccurrencesMap.get(cellValue);
rowList.add(Integer.valueOf(currentRowNumber));
}
}
// Identify duplicates (values appearing in more than 1 row)
boolean hasDuplicates = false;
for (Object oEntry : valueOccurrencesMap.entrySet()) {
Map.Entry entry = (Map.Entry) oEntry;
String val = (String) entry.getKey();
List rowIndices = (List) entry.getValue();
if (rowIndices.size() > 1) {
hasDuplicates = true;
// Format row indices (e.g., "2, 5")
StringBuilder rowsStr = new StringBuilder();
for (int i = 0; i < rowIndices.size(); i++) {
rowsStr.append(rowIndices.get(i).toString());
if (i < rowIndices.size() - 1) {
rowsStr.append(", ");
}
}
String errorMsg = "Duplicate '" + val + "' found in grid at Row(s): " + rowsStr.toString() + ". Duplicate entries are not allowed.";
formData.addFormError(elementId, errorMsg);
LogUtil.warn("Grid Validator", errorMsg);
}
}
return !hasDuplicates;
}
// Execute validation against submitted rows
return validateGridDuplicates(element, formData, rows);
Key Benefits
- Clear Feedback: Users immediately know which rows contain duplicate data without having to scan through 30+ grid rows manually.
- Data Integrity: Prevents duplicate primary entries or conflicting allocations from being stored in relational database tables.
Top comments (0)