Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/webjetcms/webjetcms/llms.txt

Use this file to discover all available pages before exploring further.

The WebJET DataTables framework wraps datatables.net with a Spring REST back-end and a custom editor extension. A single WJ.DataTable(options) call produces a fully functional table with inline editing, search, paging, sorting, export/import, and permission-aware buttons. Column definitions are generated from Java entity annotations (see Column definitions) and passed into the constructor. The REST endpoint is implemented by extending DatatableRestControllerV2 (see REST controller).

Basic example

let galleryTable = null;
window.domReady.add(function () {

    let url = "/admin/rest/components/gallery";

    let columns = [
        {
            data: "id",
            name: "id",
            title: "ID",
            defaultContent: '',
            className: 'dt-select-td',
            renderFormat: "dt-format-selector"
        },
        {
            data: "imageName",
            name: "imageName",
            title: "File name",
            renderFormat: "dt-format-text",
            renderFormatLinkTemplate: "javascript:;",
            renderFormatPrefix: '<i class="ti ti-pencil"></i> ',
            className: "dt-row-edit",
            editor: { type: "text" }
        },
        {
            data: "imagePath",
            name: "imagePath",
            title: "Directory",
            renderFormat: "dt-format-text",
            editor: {
                type: "text",
                attr: { "data-dt-field-hr": "after" }
            }
        }
    ];

    galleryTable = WJ.DataTable({
        url: url,
        serverSide: true,
        columns: columns,
    });
});
<table class="datatableInit table cardView cardViewS"></table>

WJ.DataTable(options) constructor

Required options

url
string
required
Base URL of the REST service. The datatable appends /all to retrieve all records (unless noAll is set), findByColumns for searching, and /editor for saving.
columns
array
required
Column definitions. Generate these from Java annotations using layout.getDataTableColumns('com.example.MyEntity') in Thymeleaf/Pug templates.

Display and paging

serverSide
boolean
default:"false"
When true, paging, ordering, and searching are delegated to the server via REST calls. When false, all data is loaded upfront and processed client-side.
paging
boolean
default:"true"
Set to false to disable pagination and render all returned rows.
order
array
Default sort order. Because Thymeleaf evaluates [[...]] expressions, build the array through a variable:
var order = [];
order.push([5, 'desc']);

configurationDatatable = WJ.DataTable({
    url: "/admin/v9/settings/configuration",
    columns: columns,
    order: order
});
stateSave
boolean
default:"true"
Set to false to stop the browser from remembering column order and layout between sessions.
autoHeight
boolean
default:"true"
By default the table stretches to fill the available window height. Set to false to use natural content height instead.

Identifiers

id
string
default:"datatableInit"
Unique identifier for the datatable. Required when multiple datatables exist on one page.
editorId
string
Unique identifier for the editor instance. Defaults to id.

Editor tabs

tabs
array
Tab definitions for the editor dialog. Each entry has at minimum id and title. Optional attributes:
  • selected — marks the default tab.
  • className — add hide-on-create, hide-on-edit, or hide-on-duplicate.
  • perms — hide the tab if the user lacks the named permission.
  • hideOnCreate / hideOnEdit — boolean shortcuts.
  • content — raw HTML to inject; the tab will have no editor fields.
let tabs = [
    { id: 'basic',       title: 'Basic',    selected: true  },
    { id: 'metadata',    title: 'Metadata', selected: false, className: 'hide-on-create' },
    { id: 'history',     title: '[[#{editor.tab.history}]]', hideOnCreate: true }
];

galleryTable = WJ.DataTable({ url, columns, tabs });

AJAX event hooks

onXhr
function
Called after data loads. Signature: function(TABLE, e, settings, json, xhr).
onPreXhr
function
Called before each request. Use it to append extra parameters (prefix with fixed_ to distinguish from datatable parameters):
onPreXhr: function(TABLE, e, settings, data) {
    data.fixed_searchFilterBotsOut = $('#botFilterOut').is(':checked');
}
onEdit
function
Called when a row’s edit link is clicked. Signature: function(TABLE, row, dataAfterFetch, dataBeforeFetch). Open the standard editor with TABLE.wjEdit(row).
onRowCallback
function
Called for each rendered row. Use it to apply CSS classes dynamically:
onRowCallback: function(TABLE, row, data) {
    if (data.active === false) $(row).addClass("is-not-public");
}

Pre-fetch behaviour

fetchOnEdit
boolean
default:"false"
When true, a REST call refreshes the current record’s data from the server before the editor opens, ensuring you always edit the latest version.
fetchOnCreate
boolean
default:"false"
When true, a REST call with ID -1 fetches default values for a new record before the create editor opens.

Button and permission control

hideButtons
string
Comma-separated list of button names to hide, e.g. "create,import,export".
perms
object
Map each operation to a permission name. Hides the toolbar button and the corresponding editor action button when the user lacks the right:
perms: {
    create:    'addPage',
    edit:      'pageSave',
    duplicate: 'pageSaveAs',
    remove:    'deletePage'
}
Check permissions at runtime with TABLE.hasPermission('create').
Front-end permission checks are a UX convenience only. Always enforce permissions in the REST service or service layer as well.
editorButtons
array
Custom buttons that replace the default Save button in the editor:
editorButtons: [
    {
        text: '<i class="fal fa-check"></i> Save',
        action: function() { this.submit(); }
    }
]

Data initialisation

initialData
object
Pre-loaded data used on the first render, avoiding an extra REST call. Set initialData.forceData = true to use empty initial data unconditionally.
Pre-applied filter values keyed by CSS selector. Applied before the first REST call:
{
    ".dt-filter-from-dayDate": "06.06.2022",
    ".dt-filter-to-dayDate":   "22.08.2022"
}
summary
object
Displays a footer row that sums selected numeric columns:
summary: {
    mode:    "all",   // "all" | "visible" | "datatable"
    columns: ["visits", "sessions", "uniqueUsers"],
    title:   "[[#{components.summary.total_title}]]"
}
  • mode: "all" — sums across all pages (calls /sumAll when serverSide: true).
  • mode: "visible" — sums only the current page.
  • mode: "datatable" — uses DatatablePageImpl.summary returned by the REST service.
When serverSide: true and mode: "all", the footer totals do not change as the user filters the table.

Other options

noAll
boolean
default:"false"
Prevents appending /all to the URL. Search will not work when set.
removeColumns
string
Comma-separated column names to suppress from the display, e.g. "whenToPublish,datePublished".
keyboardSave
boolean
default:"true"
Set to false to disable Ctrl+S/Cmd+S saving in the editor.
editorLocking
boolean
default:"true"
Set to false to turn off the concurrent-edit notification service.
nestedModal
boolean
default:"false"
Set to true for a datatable embedded as a field inside another editor. Adds the DTE_nested_modal CSS class.

Column renderFormat values

The renderFormat property controls how cell values are presented and which header options appear.
ValueDescription
dt-format-selectorCheckbox to select the row; use as the first column
dt-format-noneNo header options
dt-format-textPlain text with HTML escaping
dt-format-text-wrapSame as text but wraps; auto-set for TEXTAREA fields
dt-format-selectRenders from editor.options
dt-format-checkboxHTML checkbox
dt-format-boolean-true / dt-format-boolean-yes / dt-format-boolean-oneBoolean display
dt-format-number / dt-format-percentageNumeric display
dt-format-number--decimal / dt-format-percentage--decimalDecimal numeric
dt-format-number--textRounded number; large values shown as e.g. 10k
dt-format-filesizeFile size as 10.24 kB
dt-format-date / dt-format-date-timeDate/time with from–to filter
dt-format-date--text / dt-format-date-time--textDate as text
dt-format-linkText as hyperlink; use with renderFormatLinkTemplate
dt-format-imageThumbnail preview with link and text
dt-format-image-notextThumbnail preview only
dt-format-mailText as mailto: link
dt-row-editInline row-edit trigger
Set renderFormatLinkTemplate to a URL pattern (e.g. "/temps-list.html" or "javascript:;") and renderFormatPrefix for a leading icon:
{
    data: "imageName",
    name: "imageName",
    title: "File name",
    renderFormat: "dt-format-text",
    renderFormatLinkTemplate: "javascript:;",
    renderFormatPrefix: '<i class="ti ti-pencil"></i> ',
    className: "dt-row-edit"
}

Displaying raw HTML

By default, DataTables escapes HTML to prevent XSS. To allow HTML rendering in a cell, add the allow-html CSS class:
@DataTableColumn(
    inputType = DataTableColumnType.TEXTAREA,
    title = "[[#{admin.conf_editor.value}]]",
    className = "allow-html"
)
private String value;
Use allow-html only for trusted content. Allowing raw HTML from untrusted user input creates XSS vulnerabilities.

Row styling

Rows can receive CSS classes to indicate status. Use BaseEditorFields.addRowClass(String) in your EditorFields implementation:
public class DocEditorFields extends BaseEditorFields {
    public void fromDocDetails(DocDetails doc) {
        // Bold for the default page of a directory
        if (groupDetails != null && doc.getDocId() > 0
                && groupDetails.getDefaultDocId() == doc.getDocId()) {
            addRowClass("is-default-page");
        }
        // Red for disabled pages
        if (doc.isAvailable() == false) addRowClass("is-not-public");
    }
}
Alternatively, apply classes in JavaScript via onRowCallback:
onRowCallback: function(TABLE, row, data) {
    if (data.active === false) $(row).addClass("is-not-public");
}
Built-in row CSS classes:
ClassAppearance
is-disabledRed — inactive item
is-disapprovedRed — unapproved item
is-not-publicRed — non-public item
is-default-pageBold — main page of a directory
The framework reads data.editorFields.rowClass on each row and applies it automatically via the rowCallback option.

Status icons

Status icons display record state inline in a dedicated column backed by EditorFields. Use BaseEditorFields.addStatusIcon(String className) to add icon CSS classes, then define the column using @DataTableColumn with the name statusIcons:
public class DocEditorFields extends BaseEditorFields {

    @DataTableColumn(
        inputType = DataTableColumnType.SELECT,
        title = "webpages.icons.title",
        hiddenEditor = true, hidden = false, visible = true,
        sortAfter = "id", className = "allow-html", orderable = false,
        editor = { @DataTableColumnEditor(
            options = {
                @DataTableColumnEditorAttr(
                    key   = "<i class=\"ti ti-map-pin\"></i> [[#{webpages.icons.showInMenu}]]",
                    value = "showInMenu:true"),
                @DataTableColumnEditorAttr(
                    key   = "<i class=\"ti ti-map-pin-off\"></i> [[#{webpages.icons.notShowInMenu}]]",
                    value = "showInMenu:false"),
                @DataTableColumnEditorAttr(
                    key   = "<i class=\"ti ti-lock-filled\"></i> [[#{webpages.icons.onlyForLogged}]]",
                    value = "passwordProtected:notEmpty"),
                @DataTableColumnEditorAttr(
                    key   = "<span style=\"color: #FF4B58\">[[#{webpages.icons.disabled}]]</span>",
                    value = "available:false"),
                @DataTableColumnEditorAttr(
                    key   = "<i class=\"ti ti-external-link\"></i> [[#{webpages.icons.externalLink}]]",
                    value = "externalLink:notEmpty"),
                @DataTableColumnEditorAttr(
                    key   = "<i class=\"ti ti-eye\"></i> [[#{webpages.icons.notSearchable}]]",
                    value = "searchable:false")
            }
        )}
    )
    private String statusIcons;

    public void fromDocDetails(DocBasic doc, boolean loadSubQueries) {
        if (doc.isShowInMenu()) addStatusIcon("ti ti-map-pin");
        else                    addStatusIcon("ti ti-map-pin-off");
        if (Tools.isNotEmpty(doc.getExternalLink()))    addStatusIcon("ti ti-external-link");
        if (doc.isSearchable() == false)                addStatusIcon("ti ti-eye-off");
        if (Tools.isNotEmpty(doc.getPasswordProtected())) addStatusIcon("ti ti-lock-filled");
    }

    public String getStatusIcons() {
        return getStatusIconsHtml();
    }
}
The value in each option follows the pattern property:condition and drives server-side filtering. Supported conditions:
PatternMeaning
property:trueAttribute is true
property:falseAttribute is false
property:notEmptyAttribute is not empty
property:emptyAttribute is null or empty string
property:%text%Attribute contains text (LIKE search)
property:!%text%Attribute does not contain text (NOT LIKE)

JavaScript API

// Selected rows data
galleryTable.rows({ selected: true }).data();

// Change the REST URL and reload
galleryTable.setAjaxUrl("/admin/rest/nova-url");
galleryTable.ajax.reload();

// Apply a filter and redraw
galleryTable.columns(3).search("^" + virtualPath + "$").draw();

// Set JSON into the current editor
EDITOR.setJson(json);
// Currently edited JSON object
EDITOR.currentJson;

// Options from the last REST response (for number-to-label conversion in export)
TABLE.DATA.jsonOptions;
// Full URL of the last REST call
TABLE.DATA.urlLatest;
// All parameters of the last REST call
TABLE.DATA.urlLatestParams;

// Show / hide toolbar buttons (name = value of data-dtbtn attribute)
TABLE.hideButton("create");
TABLE.hideButtons(['import', 'export']);
TABLE.showButton("export");

// Turn off cell-edit mode (always call when switching tab content)
TABLE.cellEditOff();

// Recalculate page length (e.g. after changing image size in gallery)
TABLE.calculateAutoPageLength(updateLengthSelect);

Execute a server-side action

// Signature:
// nejakaTable.executeAction(action, doNotCheckEmptySelection, confirmText, noteText, customData, forceIds)

// Simple action requiring row selection
galleryTable.executeAction("rotate");

// Action that does not require a selection, with confirmation dialog
cacheObjectsTable.executeAction(
    "deletePictureCache",
    true,
    "[[#{components.data.deleting.imgcache.areYouSure}]]",
    "[[#{components.data.deleting.imgcache.areYouSureNote}]]"
);
The call posts to the REST endpoint /action/{action} on the server. The following events fire afterward:
  • WJ.DT.executeAction — on successful completion.
  • WJ.DT.executeActionCancel — on failure or when the user cancels.

Adding toolbar buttons

galleryTable.button().add(buttonCounter++, {
    text: '<i class="ti ti-repeat"></i>',
    action: function (e, dt, node) {
        galleryTable.executeAction("rotate");
    },
    init: function (dt, node, config) {
        // Show only when at least one row is selected
        $.fn.dataTable.Buttons.showIfRowSelected(this, dt);
    },
    className: 'btn btn-outline-secondary',
    attr: {
        title: 'Rotate',
        'data-toggle': 'tooltip'
    }
});
init visibility helpers:
HelperBehaviour
showIfRowSelected(this, dt)Active when ≥ 1 row selected
showIfRowUnselected(this, dt)Active when no rows selected
showIfOneRowSelected(this, dt)Active when exactly 1 row selected

Events

EventWhen fired
WJ.DTE.openEditor window is opening
WJ.DTE.openedEditor window is fully visible
WJ.DTE.closeEditor window is closing
WJ.DTE.forceReloadREST service requested a datatable refresh
WJ.DTE.tabclickTab clicked in the editor
WJ.DTE.submitclickSave button clicked
WJ.DTE.closeclickClose button clicked
WJ.DTE.xhrfetchData loaded via fetchOnEdit
WJ.DTE.resizeEditor window maximised/minimised
Listen for a table reload to update a JS tree:
window.addEventListener('WJ.DTE.forceReload', (e) => {
    $('#SomStromcek').jstree(true).refresh();
}, false);