Overview
Joget DX provides robust built-in Form Grids for tabular data entry. However, enterprise applications frequently require advanced grid behaviors such as custom field-level validators, dynamic column visibility toggles based on process type, formatted currency/number inputs, and custom row actions.
By embedding Tabulator JSβa lightweight, feature-rich JavaScript table libraryβinto a Joget Custom HTML element, developers can deliver a high-performance spreadsheet-like user experience inside Joget forms while keeping data synchronized with Joget form storage.
How It Works
-
Tabulator Initialization: The script dynamically initializes Tabulator on an HTML container (
<div id="custom-grid"></div>). -
Dynamic Header & Visibility Rules: Depending on the selected transaction or document type, column definitions are dynamically updated to hide or show relevant fields using
setColumns(). -
Real-Time Validation & Formatting: Custom JavaScript formatters and validators enforce max length rules, currency formatting (with commas), date pickers (
flatpickr), and conditional read-only logic. - Data Synchronization: On form submission or cell updates, table data is serialized into JSON format and synced with a hidden Joget Form field so that Joget stores the grid payload seamlessly.
Where to Use in Joget
- Form Builder: Place the script inside a Custom HTML field in your Joget Form. Bind the table output to a hidden Text Area or Text Field to store the JSON dataset.
- Userviews: Embed within custom Userview pages to display dynamic interactive reports with inline editing capability.
Full Code
<!-- Tabulator CSS & JS Dependencies -->
<link href="https://unpkg.com/tabulator-tables@5.4.4/dist/css/tabulator.min.css" rel="stylesheet" />
<script type="text/javascript" src="https://unpkg.com/tabulator-tables@5.4.4/dist/js/tabulator.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/luxon@3.3.0/build/global/luxon.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
<script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
<!-- Grid Container -->
<div id="tabulator-custom-grid"></div>
<style>
.flatpickr-calendar { font-size: 12px; }
.flatpickr-input { font-size: 12px; padding: 4px 8px; }
.tabulator .invalid { border: 2px solid #ff4d4f !important; background-color: #fff1f0 !important; }
</style>
<script>
$(function () {
// Reference hidden Joget storage element
const hiddenStorageField = document.getElementById("list_grid_items");
// Number & Amount Validators
function amountValidator(cell, value) {
if (!value) return true;
const strValue = String(value);
const parts = strValue.split(".");
const wholePart = parts[0];
const decimalPart = parts[1] || "";
return wholePart.length <= 13 && decimalPart.length <= 2;
}
// Currency & Thousand Separator Formatter
const commasFormatter = () => {
return (cell) => {
let value = cell.getValue();
if (!value) return "";
const [integerPart, decimalPart] = String(value).split('.');
const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
return decimalPart ? `${formattedInteger}.${decimalPart}` : formattedInteger;
};
};
// Custom Date Picker Editor
const customDateEditor = function (cell, onRendered, success, cancel, editorParams) {
const input = document.createElement("input");
input.type = "text";
if (cell.getValue()) {
input.value = cell.getValue();
}
flatpickr(input, {
dateFormat: "Y/m/d",
onClose: function (selectedDates, dateStr) {
success(dateStr);
}
});
onRendered(function () {
input.focus();
});
return input;
};
// Column Definitions
const columnsConfig = [
{ title: "#", formatter: "rownum", width: 50, headerSort: false, frozen: true },
{
title: "Item Code",
field: "item_code",
editor: "input",
width: 120,
validator: "required",
headerSort: false
},
{
title: "Category",
field: "category",
editor: "list",
editorParams: { values: ["General", "Transfer", "Adjustment", "Tax"] },
width: 130,
headerSort: false
},
{
title: "Debit Amount",
field: "debit_amount",
editor: "input",
width: 140,
formatter: commasFormatter(),
validator: amountValidator,
headerSort: false
},
{
title: "Credit Amount",
field: "credit_amount",
editor: "input",
width: 140,
formatter: commasFormatter(),
validator: amountValidator,
headerSort: false
},
{
title: "Posting Date",
field: "posting_date",
editor: customDateEditor,
width: 130,
headerSort: false
},
{
title: "Description",
field: "description",
editor: "input",
validator: "maxLength:50",
headerSort: false
}
];
// Initialize Tabulator Grid
const table = new Tabulator("#tabulator-custom-grid", {
data: hiddenStorageField && hiddenStorageField.value ? JSON.parse(hiddenStorageField.value) : [{}],
layout: "fitColumns",
columns: columnsConfig,
history: true,
reactiveData: true
});
// Sync grid data to Joget hidden input on cell edit
table.on("cellEdited", function (cell) {
if (hiddenStorageField) {
hiddenStorageField.value = JSON.stringify(table.getData());
}
});
});
</script>
Example Use Cases
- Financial Journal Voucher Entry: Entering GL debit/credit entries where amounts require real-time thousands formatting and balance checking.
- Complex Inventory Adjustments: Adding batch inventory lines with custom unit validation and date selection.
- Timesheet & Line Item Expense Claims: Managing multi-row claims with dynamic column visibility based on claim type (e.g., travel vs. office supplies).
Customization Tips
-
Dynamic Column Toggling: Use
table.setColumns(newColumnConfig)inside anonChangeevent of a Joget Select Box to dynamically show or hide columns based on user choices. -
Conditional Cell Backgrounds: Add CSS classes dynamically in formatters (e.g.,
cell.getElement().classList.add("invalid")) to highlight invalid entries immediately. -
Handling Large Datasets: Enable Tabulator pagination (
pagination: "local",paginationSize: 10) to maintain browser rendering speed for large tables.
Key Benefits
- Enhanced UX: Provides spreadsheet-like editing directly inside standard web forms.
- Instant Client Validation: Prevents invalid submissions before the form touches the server.
- Seamless Integration: Uses standard JSON stringification to bind directly to Joget form storage.
Security Note
Always re-validate grid JSON data on the server side using a BeanShell Form Binder or Form Validator plugin prior to storing values in backend database tables. Never rely exclusively on client-side JavaScript validation for security-sensitive fields.
Final Thoughts
Tabulator JS offers a powerful bridge between standard Joget form components and highly customized data entry interfaces. By combining client-side formatting with standard Joget form fields, developers can satisfy demanding UX requirements without sacrificing platform maintainability.
Top comments (0)