AI: Canonical properties reference
This is the authoritative property reference for AI JSON generation. Only properties listed here are supported. Do not invent or guess properties not found in this document.
Base properties (all elements)
Every element must have these three fields:
{
"name": "fieldName",
"label": "Field Label",
"type": "text"
}Additional base properties available on all element types:
| Property | Type | Description |
|---|---|---|
description | string | Subtitle or help text shown below the label |
hidden | boolean | Statically hides the element |
width | string | Width (e.g. "100%", "300px") |
tabletWidth | string | Width on tablet |
mobileWidth | string | Width on mobile |
dependsOn | string[] | Limit expression recalculation to these field names |
logicExecutionMode | "onBlur" | "onChange" | "onInput" | When logic/expression is re-evaluated |
validationExecutionMode | "onBlur" | "onChange" | "onInput" | When validators are re-evaluated |
status | "default" | "readonly" | Interaction status override |
labelTooltip | string | Tooltip shown on label hover |
fitContent | boolean | Width fits content |
Logic fields (element level only, never inside validators)
These fields accept JEXL expressions using {fieldName} placeholder syntax.
| Field | Return type | Behavior |
|---|---|---|
visibleIf | boolean | Element visible when true |
disableIf | boolean | Element disabled when true |
requireIf | boolean | Element required when true |
readonlyIf | boolean | Element readonly when true |
resetIf | boolean | Element value cleared when true |
expression | any | Result is set as element value; also for setElementProperty calls |
All logic fields must contain valid JEXL expressions only.
Braces always read a field, so an expression cannot hold an object literal. {qty} is the value of the field called qty, which means [{label: 'A', value: 1}] looks for a field named label: 'A', value: 1 and comes out as [null]. nvb_execute refuses such an expression with badExpression. Build objects with obj() instead, which takes key and value pairs:
"[obj('label', 'Yes', 'value', {yesCount}), obj('label', 'No', 'value', {noCount})]"Arrays of plain values such as [1, 2, 3] work as they are.
There is no hideIf, and no readOnlyIf with a capital O. These six keys are the entire list and they are matched exactly. Anything else is an ordinary property as far as the runtime is concerned: it is stored, it is never evaluated, and no dependency is registered for it. What you get is a form that looks correct in the JSON and does nothing at all, with no error anywhere to point at. Write the visible condition rather than the hidden one, because visibleIf states when the element is shown, so a hide condition has to be inverted, not renamed.
Execution mode
| Field | Default | Behavior |
|---|---|---|
logicExecutionMode | onBlur | When the logic fields above are re-evaluated |
validationExecutionMode | onBlur | When validators are re-evaluated |
Both fall back to onBlur when the property is missing, so logic only re-runs after the field loses focus. To anyone typing in the form that reads as broken logic, so set onChange on every element whose logic or validation has to follow the typing, unless another mode was asked for.
Static state fields
| Field | Type | Description |
|---|---|---|
required | boolean | Always required |
disabled | boolean | Always disabled |
readOnly | boolean | Always read-only |
Validators
validators is an array of IValidator objects. Exact interface:
interface IValidator {
type?: string; // validator type (required)
message?: string; // error message shown to user
value?: string | number; // threshold (minLength, maxLength, min, max, minDate, maxDate)
condition?: string; // JEXL, the FAILING check; error shown while this evaluates to true
applyIf?: string; // JEXL, validator runs only when this is truthy
}Forbidden inside validators: expression, visibleIf, requireIf, disableIf, readonlyIf, resetIf.
{
"validators": [
{ "type": "minLength", "value": 3, "message": "At least 3 characters" },
{ "type": "maxLength", "value": 100, "message": "Too long", "applyIf": "notEmpty({name})" },
{ "type": "min", "value": 0, "message": "Cannot be negative" },
{ "type": "max", "value": 999, "message": "Too large" },
{ "type": "minDate", "value": "today", "message": "Cannot be in the past" },
{ "type": "maxDate", "value": "today", "message": "Cannot be in the future" }
]
}Events / Actions
events is an array of IElementActionConfig objects. Key fields:
| Field | Type | Values / Notes |
|---|---|---|
trigger | string | click submit input change blur focus beforeLoad onLoad afterLoad always. Prefer a named trigger; always skips trigger filtering entirely and is only for host elements whose events are not gestures (a router outlet's activate/deactivate), so on a normal element it fires on load and on every gesture. |
type | string | navigate dataSource setValue setOptions reloadElements toast dialog validate submit |
label | string | Button label (when rendered as action button) |
icon | string | Icon name |
condition | string | JEXL, action runs only when truthy |
validateForm | boolean | Validate before executing |
confirmEnabled | boolean | Show confirmation dialog before action |
confirmTitle | string | Confirm dialog title |
confirmMessage | string | Confirm dialog body |
By action type:
navigate: navigateTo (URL/route), openInNewTab (boolean)
dataSource: dataSourceName, responseMode (none download setValue), responseDataPath, responseTargetElement, reloadCurrentElementAfterSuccess, reloadOnReturnElementNames
toast: toastTitle, toastMessage, toastVariant (error warning info success), toastPosition (top-left top-center top-right bottom-left bottom-center bottom-right), toastAutoHide, toastAutoHideMs
dialog: dialogName, dialogOperation (open close toggle)
setValue: setValueTargetPath, setValueMode (contextPath template json), setValueValue
setOptions: setOptionsTargetElement, setOptionsMode (contextPath template json), setOptionsValue
reloadElements: reloadElementNames (comma-separated)
validate: no extra fields. Validates the whole form and shows field errors, same as the built-in Validate toolbar button
submit: no extra fields. Validates the form, then fires onComplete with { isValid, data, issues }, same as the built-in Submit toolbar button
Button appearance for action: buttonVariant (filled outlined text), buttonTone (primary risk neutral), hideText, useCustomButtonStyle
Per-type additional properties
text
| Property | Type | Notes |
|---|---|---|
placeholder | string | |
minlength | number | |
maxlength | number | |
showMaxLengthCounter | boolean | Show counter when maxlength is set |
pattern | string | Regex pattern |
maskType | string | Exactly: none phoneIntl date time dateTime digits custom. No other mask names exist |
maskPattern | string | Custom mask (when maskType is custom) |
inputMode | string | text search email tel url numeric decimal |
spellcheck | boolean | |
autocomplete | string | Browser autocomplete hint |
requiredMessage | string | Custom required error message |
textarea
Same as text plus:
| Property | Type | Notes |
|---|---|---|
rows | number | Visible row count |
number
| Property | Type | Notes |
|---|---|---|
placeholder | string | |
min | number | Minimum value |
max | number | Maximum value |
step | number | Increment step |
inputMode | string | numeric decimal |
valueStorageType | string | number (default) or string |
visualFormatEnabled | boolean | Enable visual number formatting |
visualFormatLocale | string | Locale for formatting (e.g. "lt-LT") |
visualFormatUseGrouping | boolean | Thousands separator |
visualFormatMinFractionDigits | number | |
visualFormatMaxFractionDigits | number | |
requiredMessage | string |
datepicker
Exact type string: "datepicker" (all lowercase)
| Property | Type | Notes |
|---|---|---|
placeholder | string | |
pickerMode | string | date (default) or datetime |
includeSeconds | boolean | For datetime mode |
format | string | Output format. Exactly one of "" (locale) YYYY-MM-DD DD/MM/YYYY MM/DD/YYYY DD.MM.YYYY YYYY/MM/DD custom. Lowercase Angular-style patterns such as yyyy-MM-dd are not valid |
customFormat | string | The pattern used when format is custom |
minValue | string | Min selectable date (JEXL or static) |
maxValue | string | Max selectable date (JEXL or static) |
requiredMessage | string |
dateRange
| Property | Type | Notes |
|---|---|---|
placeholder | string | |
format | string | |
minValue | string | |
maxValue | string | |
requiredMessage | string |
Value shape: { "dateFrom": "YYYY-MM-DD", "dateTo": "YYYY-MM-DD" }
select / multiSelect / radio / checkbox / autocomplete
| Property | Type | Notes |
|---|---|---|
placeholder | string | Real on autocomplete, listBox, selectButton. On select / multiSelect it sets only the search box hint inside the open dropdown, and only when showSearch is on; the closed-state "Select..." text comes from the UI translation select.placeholder / multiSelect.placeholder, not from this property |
options | IOption[] | Static options: [{ "value": "x", "label": "X" }] |
dataSource | IElementDataSource | Dynamic options from datasource |
showSearch | boolean | Search box in dropdown (select) |
strictOptions | boolean | Value must be from options list |
filterIfEqual | string | Filter options when datasource field equals value |
filterIfNotEqual | string | Filter options when datasource field not equals value |
optionTemplate | string | Inline HTML template for each option |
optionTemplateName | string | Template name reference |
showInline | boolean | Lay the choices out in a row (radio, checkbox) |
requiredMessage | string |
Value shapes:
select/radio/autocomplete: single valuemultiSelect/checkbox: array
autocomplete (search data source)
For lists too large to load upfront, the element calls its own search source while the user types. This is separate from dataSource, which stays for static option lists.
| Property | Type | Notes |
|---|---|---|
searchDataSourceName | string | Name of a top-level data source. Its URL or body uses {query} for the typed text and {limit} for the result limit |
searchItemsPath | string | Path to the array in the response, e.g. data.items. Optional: items, data, results, rows, content (also under data.) are tried when empty |
searchLabelKey | string | Field shown in the dropdown, dot paths allowed (country.name). Empty tries label, name, title, text |
searchValueKey | string | Field stored as the value. Empty stores the whole record as an object |
searchResultLimit | number | Most records the source returns. An answer shorter than this is complete, so a longer query that extends the answered one is filtered locally instead of calling again. Empty calls on every change |
debounceMs | number | Wait after the last keystroke. Default 1000, max 10000 |
minSearchLength | number | Characters before the first call. Default 3 |
queryContextKey | string | Extra name for the typed text besides query |
maxSuggestions | number | Most suggestions shown. Default 20 |
forceSelection | boolean | Clear text that matches no suggestion on blur. Turn it on when storing whole records |
lazyLoad | boolean | Legacy: search through dataSource instead. Ignored when searchDataSourceName is set |
{
"name": "city",
"type": "autocomplete",
"label": "City",
"searchDataSourceName": "citySearch",
"searchLabelKey": "name",
"searchResultLimit": 100,
"minSearchLength": 2,
"forceSelection": true
}with a source such as { "name": "citySearch", "type": "rest", "params": { "method": "GET", "url": "/api/cities?q={query}&limit={limit}" } }. Value shape here: the whole record, e.g. { "id": 21, "name": "Riga", "country": { "code": "LV" } }; with "searchValueKey": "id" it would be 21.
singleCheckbox / toggleSwitch / toggleButton
Value shape: boolean
| Property | Type | Notes |
|---|---|---|
checkboxLabel | string | Text rendered next to the box (singleCheckbox). label is the field label above it; for a bare checkbox row set label to "" and put the wording here |
checkedValue | any | Value when checked (toggleButton only) |
uncheckedValue | any | Value when unchecked (toggleButton only) |
singleCheckbox has no checkedValue / uncheckedValue. It always stores true / false.
phoneInput
| Property | Type | Notes |
|---|---|---|
placeholder | string | |
defaultCountryCode | string | ISO code of the preselected country: LT LV EE PL DE SE NO DK FI GB IE US |
allowedCountryCodes | string[] | Restrict the country dropdown to these ISO codes |
maxlength | number | |
autocomplete | string | |
requiredMessage | string |
phoneInput has its own country dropdown. It has no maskType, maskPattern or inputMode, and the dial code is not set via defaultValue.
button
| Property | Type | Notes |
|---|---|---|
label | string | Set to "" on a button: the caption belongs in text, and a label draws an extra label row above the button |
text | string | Button text (the caption) |
icon | string | Icon name from help().icons (registered by the host, or built in), or SVG markup for a one-off icon. An unknown name draws a question mark |
iconPosition | string | left right top bottom |
iconOnly | boolean | Icon alone in a square button, in any variant. Keep the caption in text: it becomes the accessible name and the tooltip |
iconSize | number | Icon size in px. Left out it follows size (16, 18, 20); an icon-only square grows with a larger icon |
iconColor | string | Icon color when it differs from the caption |
variant | string | solid outline text |
tone | string | primary neutral success info warning risk |
color | string | Custom fill color (any CSS color or var(--nvb-color-...)) |
textColor | string | Caption color. On a solid button with a dark fill (primary, success, info, risk, or a dark color) set "var(--nvb-color-neutral-000)" |
size | string | small normal large |
loading | boolean | Show loading spinner |
badge | string | Badge text |
events | IElementActionConfig[] | Primary click actions |
menuActions | IElementActionConfig[] | Dropdown menu actions |
fileUpload
| Property | Type | Notes |
|---|---|---|
accept | string | Accepted MIME types or extensions, e.g. .pdf,image/* |
multiple | boolean | Allow multiple files |
maxFiles | number | Max number of attached files (default 5) |
maxFileSizeMb | number | Max file size in megabytes, not bytes |
storageMode | "base64" | "server" | base64 (default) keeps each file's content in the value. server uploads through uploadDataSourceName and keeps the returned fileKey. A view saved without storageMode but with an upload source counts as server |
uploadDataSourceName / downloadDataSourceName / deleteDataSourceName | string | Names of REST data sources, used only when storageMode is server |
uploadFormFieldName | string | multipart/form-data field name for the file (default file). Upload always sends single-field FormData, never JSON/base64 |
fileKeyField / fileNameField / fileTypeField / fileSizeField | string | Field names read from the server's response object (defaults key, name, contentType, size; the older fil_key/fil_name/fil_content_type/fil_size names are still recognized as fallback aliases) |
Value shape (an array of these when multiple), keyed under the element's name, the same in both modes:
{ "name": "logo.png", "size": 4507, "type": "image/png", "lastModified": 1790764181739, "contentType": "image/png", "base64": "iVBORw0KGgo..." }With storageMode: "base64" the object carries base64 (file content without the data: prefix). With "server" it carries fileKey instead. Base64 makes the value about a third larger than the file, so set maxFileSizeMb when using it. Full request/response wire contract: File upload requests.
page
The complete list. A page element accepts nothing else.
| Property | Type | Notes |
|---|---|---|
name | string | Must match the pages[*].name entry |
label | string | Page / step title |
description | string | Subtitle |
type | "page" | Required |
hideHeader | boolean | Hide the page title block |
removeBackgraund | boolean | Drop the page background. Spelled exactly like this, the typo is part of the schema |
pageBackgroundColor | string | |
pagePadding | string | |
mobilePadding | string | |
visibleIf / disableIf / readonlyIf | string | JEXL |
A page's children are not listed here. They live in pages[*].rows; see Layout model.
panel
| Property | Type | Notes |
|---|---|---|
headerTemplate | string | Inline HTML for panel header |
headerTemplateName | string | Template name reference |
maxHeight | string | Max panel height |
panelPadding | string | Internal padding |
mobilePadding | string | Mobile padding |
panelRadius | string | Border radius |
panelShadow | string | Box shadow CSS |
showBorder | boolean | Show panel border |
panelBorderWidth | string | Border width (when showBorder) |
panelBorderColor | string | Border color (when showBorder) |
panelBackgroundColor | string | Background color |
titleUnderline | boolean | Show underline below title |
contentJustify | string | Flexbox justify: flex-start center flex-end space-between space-around space-evenly |
contentAlign | string | Flexbox align: flex-start center flex-end stretch |
contentGap | string | Gap between child elements |
titleUnderlineColor | string | Underline color (when titleUnderline) |
titleUnderlineWidth | string | Underline thickness (when titleUnderline) |
resetChildrenOnHide | boolean | Clear descendant field values when the panel becomes hidden |
sectionMode | boolean | Turns the panel into a page section: a full width band with no border, content centred at sectionContentWidth. The building block of web pages; pair it with settings.renderMode: "canvas" |
sectionTheme | "light" | "dark" | dark switches every colour inside the section to the dark palette, so text, buttons and inputs on it stay readable |
sectionContentWidth | string | Max width of the content inside the band (default 1120px) |
sectionPaddingY | string | Space above and below the content (default grows from 48px to 96px with the screen) |
sectionMinHeight | string | Minimum height, e.g. "80vh" for a hero band |
sectionVerticalAlign | "start" | "center" | "end" | Content position when sectionMinHeight makes the band taller than its content |
backgroundImage | string | Section background image URL (with sectionMode) |
backgroundOverlayColor | string | Colour over the background image so text stays readable, e.g. rgba(0, 0, 0, 0.45) |
A panel never lists its children. There is no rows, columns, children, elements or items property on panel. Child fields are attached in the layout tree, on the column that references the panel. See Layout model.
objectPanel
Same properties as dynamicPanel, without the row controls (addRowButtonText, removeRowButtonText, emptyMessage, disallowAddRows, disallowDeleteRows, maxRows, confirmRowDeletion and the confirm texts). It is a dynamic panel with exactly one entry: every element laid out under it, at any depth and through any plain layout containers, stores its value at <objectPanel>.<name>. Its children are defined in its template, so their names only have to be unique inside the panel.
"pages": [{ "name": "page1", "rows": [{ "columns": [{
"elementRef": "address",
"rows": [{ "columns": [{ "elementRef": "city" }, { "elementRef": "street" }] }]
}] }] }],
"elements": {
"address": {
"name": "address", "type": "objectPanel", "label": "Address",
"template": {
"city": { "name": "city", "type": "text", "label": "City" },
"street": { "name": "street", "type": "text", "label": "Street" }
}
}
}Value shape: { "address": { "city": "...", "street": "..." } }. Expressions and data source params reference {address.city}; inside the panel {panel.city} is the sibling. A nested objectPanel nests the object, also inside a dynamicPanel entry (items[0].address.city). Its own children go in its own template, not in the outer panel's: the outer template holds the inner panel, the inner template holds the inner fields.
dynamicPanel
Same as panel plus repeatable container behavior (addRowButtonText, removeRowButtonText, emptyMessage, disallowAddRows, disallowDeleteRows, disallowAddRowsIf, disallowDeleteRowsIf, maxRows, confirmRowDeletion, hideHeader). Value shape: array of objects. Children are attached the same way as for panel, via column.rows.
disallowAddRowsIf and disallowDeleteRowsIf are JEXL conditions, the same pair as on dynamicTable below: while disallowAddRowsIf is true the add button is disabled, and disallowDeleteRowsIf is checked for every entry, so an entry where it is true has no remove button. Inside them {row.field} (or {panel.field}) reads the entry, {index} is its position, {rowCount} the number of entries, and any other {field} reads the form.
dynamicTable
| Property | Type | Notes |
|---|---|---|
emptyMessage | string | Message when no rows |
addRowButtonText | string | Add row button label |
itemsPath | string | Data path for pre-populating rows |
disallowAddRows | boolean | |
disallowDeleteRows | boolean | |
disallowAddRowsIf | string | JEXL. While true, the add row button is disabled. Form fields and {rowCount}: {rowCount} >= 5 || {status} == "closed" |
disallowDeleteRowsIf | string | JEXL, checked per row: a row where it is true has no delete button. {row.field}, {index}, {rowCount} and form fields: {row.status} == "approved" |
maxRows | number | |
confirmRowDeletion | boolean | |
confirmDeleteTitle | string | |
confirmDeleteMessage | string | |
confirmDeleteConfirmLabel | string | |
confirmDeleteCancelLabel | string | |
hideRowIf | string | JEXL, hides individual rows |
columns | IDynamicTableColumn[] | Column definitions |
dataSource | IElementDataSource | Pre-populate from datasource |
Column-level totals:
| Property | Type | Notes |
|---|---|---|
columns[*].useTotals | boolean | Sum in the footer row, also published to data |
columns[*].totalToData | boolean | Publish the sum without a footer row |
A published sum lands in the data as <table>.<column>-total, for example el4.column1-total, and is read in expressions as {el4.column1-total}. Inside a dynamic panel the key carries the entry index: el1[0].el5.column3-total.
Value shape: array of objects.
table
columnsConfig entries use key (NOT name). Minimum column:
{
"key": "firstName",
"label": "First Name",
"showInTable": true
}Column type values the builder offers: text, date, dateTime, number, and element. The renderer still understands boolean html status toggleSwitch singleCheckbox so older structures keep working, but do not author new columns with them; use element with the matching elementType instead.
date, dateTime, and number format the raw value in place, no template or cell element needed:
{
"key": "created",
"label": "Created",
"type": "dateTime",
"dateIncludeSeconds": true,
"showInTable": true
}| Property | Type | Notes |
|---|---|---|
formatLocale | string | lt-LT, en-US. Empty follows the view locale |
dateFormatPattern | string | yyyy-MM-dd HH:mm:ss. Tokens yyyy yy MM dd HH hh mm ss SSS a. Wins over the locale format |
dateIncludeSeconds | boolean | dateTime only |
numberMinFractionDigits | number | Decimal places, minimum |
numberMaxFractionDigits | number | Decimal places, maximum |
numberUseGrouping | boolean | Thousands separator, defaults to true |
A column of type: "element" renders a real element in every row:
{
"key": "status",
"label": "Status",
"type": "element",
"showInTable": true,
"elementType": "select",
"element": {
"options": [{ "label": "New", "value": "new" }, { "label": "Done", "value": "done" }],
"events": [
{ "trigger": "change", "type": "dataSource", "dataSourceName": "saveStatus",
"params": [
{ "name": "id", "value": "{row.id}" },
{ "name": "status", "value": "{value}" }
] }
]
}
}elementType is the element type id (select, button, badge, datepicker, progressBar, customHtml, richTextViewer, and so on). Use richTextViewer when the row field already holds HTML and only needs rendering. element is that element's own configuration, the same keys it would have as a page element. Inside it, {row.*}, a bare sibling field name, {index}, and {value} all resolve against the row the cell belongs to.
| Property | Type | Notes |
|---|---|---|
columnsConfig | ITableColumnConfig[] | Column definitions, use key not name |
columnsConfig[*].elementType | string | Element type rendered in the cells, when type is element |
columnsConfig[*].element | object | Configuration of that hosted element (options, dataSource, events, validators) |
dataSource | IElementDataSource | The table's source, for both client-side and server-side tables. With lazyLoad: false it must return all rows in one response; with lazyLoad: true it must accept the TABLE-POST paging/filter contract below |
tableDataSourceName | string | Legacy server-side binding, still read for older structures. New structures put the source in dataSource |
lazyLoad | boolean | true = server-side paging/sort/search per request; false = load everything once |
tableItemsPath / tableTotalPath | string | Response paths for rows / total count, both optional. Common shapes (items/data/results/rows, total/totalCount/totalRecords/count/cnt) are auto-detected |
pageSize | number | Default page size |
showQuickSearch | boolean | The quick search box in the header |
quickSearchOnType | boolean | Default false: the quick search applies on the search button or Enter only. true searches while typing, after searchDebounceMs |
rowActions | ITableRowActionConfig[] | Row action buttons. These go through the generic action/data-source pipeline, not this contract |
Do not use ITableDataSourceConfig. It exists in the type definitions but is dead code, never wired to anything; the real per-table binding is the flat tableDataSourceName/tableItemsPath/tableTotalPath trio above.
When lazyLoad: true and the data source's method is the literal string TABLE-POST (sent over the wire as a real POST), NGX View Builder auto-merges the current page/sort/search state into the request body:
{
"pagingParams": { "cnt": null, "orderClause": "lastName DESC", "pageSize": 25, "skipRows": 50, "totalCountUsed": false },
"params": [ ["tenantId", "42"] ],
"extendedParams": [
{ "paramName": "quickSearch", "paramValue": { "condition": "%-%", "value": "acme" } },
{ "paramName": "status", "paramValue": { "condition": "=", "value": "active" } }
]
}condition values: %-% contains, !%-% not contains, %- starts with, -% ends with, =/!= equals/not equals, >/>=/</<= for numbers and dates. Full contract, response shape, row-action/inline-edit context, and export behavior: Table: server-side paging & filtering.
tabs / tabsPro
Contains named tab sections. Child rows assigned via column.tabRows.
accordion
Collapsible sections with label per panel.
dialog
Modal element. Configure via settings.dialog* fields:
| Setting | Notes |
|---|---|
settings.renderMode | "dialog" |
settings.dialogTitle | Dialog title. {field} tokens show that field's current value, e.g. "Order {orderNo}" |
settings.dialogDescription | Subtitle. Takes {field} tokens like the title |
settings.dialogWidth | Width |
settings.dialogShowCloseButton | boolean |
settings.dialogPadding | CSS padding |
settings.dialogMaxWidth | Max width |
settings.dialogMaxHeight | Max height |
richText / richTextViewer
Value shape: string (HTML).
messageCard
| Property | Type | Notes |
|---|---|---|
title | string | Heading |
descriptionText | string | Text under the title; typed line breaks are kept |
variant | string | Colour: info primary success warn risk neutral |
appearance | "soft" | "card" | "accent" | soft tinted banner (default), card white card with the icon in a tile above the title (feature lists, empty states), accent a coloured left rule and no box (hints inside forms) |
actionsLayout | "stacked" | "inline" | Buttons under the text (default) or beside it when the card is wide |
events | IElementActionConfig[] | Every trigger: "click" action with a label becomes a small button. Keep to one or two. Without buttonVariant/buttonTone the first is filled in the message colour and the rest are outlined |
icon / showIcon | string / boolean | Leading icon; each variant has a default |
dismissible | boolean | Close button |
initiallyVisible | boolean | Start hidden (shown by an action) |
variantMode / variantRules / variantExpression | Conditional colour |
statsCard
| Property | Type | Notes |
|---|---|---|
value | string | Main numeric/text value |
trend | string | Trend direction |
trendValue | string | Trend amount |
badge
| Property | Type | Notes |
|---|---|---|
text | string | Badge content |
tone | string | primary success warning risk neutral info |
image
| Property | Type | Notes |
|---|---|---|
src | string | Image URL or JEXL expression |
alt | string | Alt text |
objectFit | string | CSS object-fit |
icon
| Property | Type | Notes |
|---|---|---|
icon | string | Icon name |
size | string | Icon size |
tone | string | Color tone |
divider
No additional properties beyond base.
spacer
| Property | Type | Notes |
|---|---|---|
height | string | Spacer height |
pageTitle
| Property | Type | Notes |
|---|---|---|
title | string | Heading text |
subtitle | string | Subheading text |
breadcrumbs
| Property | Type | Notes |
|---|---|---|
items | array | Breadcrumb items |
iframe
| Property | Type | Notes |
|---|---|---|
src | string | Source URL |
height | string | Frame height |
scrolling | boolean |
slider
| Property | Type | Notes |
|---|---|---|
min | number | |
max | number | |
step | number | |
valueStorageType | string | number or string |
numberStepper
| Property | Type | Notes |
|---|---|---|
step | number | Increment/decrement step |
min | number | Min value |
max | number | Max value |
Value shape: number.
timePicker
| Property | Type | Notes |
|---|---|---|
placeholder | string | |
minuteStep | number | Minute increment shown in the picker |
Value shape: time string.
signaturePad
| Property | Type | Notes |
|---|---|---|
canvasHeight | number | Canvas height in px |
clearLabel | string | Clear button label |
strokeColor / strokeWidth / backgroundColor | string / number / string | Drawing style |
exportFormat | string | png or jpeg |
Value shape: signature image data (per exportFormat), keyed under the element's name.
listBox
| Property | Type | Notes |
|---|---|---|
selectionMode | string | single or multiple |
dataSource | IElementDataSource | Options from a datasource |
options | array | Static options |
maxHeight | number | List height in px |
Value shape: single value (selectionMode: "single") or array (selectionMode: "multiple").
selectButton
| Property | Type | Notes |
|---|---|---|
multiple | boolean | Allow selecting more than one option |
allowEmpty | boolean | Allow deselecting to no value |
variant | string | buttons or segmented |
orientation | string | horizontal or vertical |
size | string | small normal large |
tone | string | primary neutral success info warning risk |
Value shape: single value, or array when multiple is true.
progressBar
| Property | Type | Notes |
|---|---|---|
min / max | number | Value bounds |
expression | string | Computes the current value |
dataSource / progressDataPath / progressValuePath / progressMaxPath | - | Drive the value from a datasource |
displayType | string | linear or circular |
valueLabelMode | string | percent value fraction |
Value shape: number (0 to max, default 100).
ISettings fields
Only include settings actually needed by the form.
| Field | Type | Notes |
|---|---|---|
language | string | Required. E.g. "en", "de" |
locale | string | E.g. "en-US", "lt-LT" |
width | string | Form width |
widthUnit | "px" | "%" | |
renderMode | "page" | "dialog" | "canvas" | canvas: full width, no page card, no Prev/Next or Submit bar, no outer spacing, every page stacked into one long page. For web pages; build them from panels with sectionMode. Columns without mobileWidth stack on phones |
theme | "light" | "dark" | |
pageNavigationMode | "default" | "stepper" | |
allowStepWithoutValidation | boolean | |
pageNavigationPosition | "top" | "bottom" | "both" | "none" | |
stepperPosition | "top" | "bottom" | "both" | "none" | |
actionButtonsPosition | "top" | "bottom" | "both" | "none" | |
showSubmitButton | boolean | |
showValidateButton | boolean | |
showValidationIssuesModal | boolean | |
elementSpacing | string | Gap between rows |
customCss | string | Raw CSS injected |
lazyElementRendering | boolean | Lazy-render off-screen elements |
IDataSource fields (top-level dataSources array)
{ name, title, type, params }. type is one of rest local route websocket sse, and params is a plain object whose keys depend on that type. websocket and sse are live: they stay connected and update the view on every message.
{
"dataSources": [
{ "name": "ds1", "title": "List", "type": "rest",
"params": { "url": "/api/items", "method": "GET" } },
{ "name": "ds2", "title": "Save", "type": "rest",
"params": { "url": "/api/items/{id}", "method": "PUT", "body": { "name": "{el1}" } } },
{ "name": "ds3", "title": "Paged table", "type": "rest",
"params": { "url": "/api/items/search", "method": "TABLE-POST" } },
{ "name": "ds4", "title": "Static list", "type": "local",
"params": { "localMode": "json", "dataJson": "[{\"id\":1,\"name\":\"A\"}]" } },
{ "name": "ds5", "title": "From form data", "type": "local",
"params": { "localMode": "dataPath", "dataPath": "el9.items" } },
{ "name": "ds6", "title": "Route data", "type": "route",
"params": { "routeDataKey": "record", "routeDataPath": "data" } },
{ "name": "ds7", "title": "Live feed", "type": "websocket",
"params": { "url": "wss://host/feed", "protocols": "", "message": "{}",
"messagePath": "payload", "messageMode": "pushValuesInArray" } },
{ "name": "ds8", "title": "Order progress", "type": "sse",
"params": { "url": "/api/orders/{orderId}/events", "events": "order.updated",
"messagePath": "", "messageMode": "replaceCurrent" } }
]
}type | params keys |
|---|---|
rest | url (required), method (GET POST PUT PATCH DELETE, or TABLE-POST for a lazy table), body (alias payload) |
local | localMode (json or dataPath), then dataJson (a JSON string) and dataFunction, or dataPath |
route | routeDataKey, routeDataPath |
websocket | url, protocols, message, messagePath, messageMode (replaceCurrent or pushValuesInArray) |
sse | url (a text/event-stream endpoint), events (comma separated event types, empty for all), messagePath, messageMode (replaceCurrent or pushValuesInArray). Listen only: nothing is sent back, so a sendMessage action does not apply. |
URL and body templates use single braces: {el1}, {row.id}, {__variables.tenantId}.
IElementDataSource (element-level dataSource field)
{
"dataSource": {
"name": "loadCities",
"useFor": "option",
"optionValue": "id",
"optionLabel": "name",
"refreshPaths": ["countryField"],
"params": [{ "name": "country", "value": "{countryField}" }]
}
}| Field | Type | Notes |
|---|---|---|
name | string | References a top-level dataSource name |
useFor | "option" | "value" | Whether datasource feeds options or element value |
optionValue | string | Property path for option value |
optionLabel | string | Property path for option label |
filterOptionsBy | string | JEXL expression using item.* to filter options |
params | IDataSourceParamMapping[] | [{ "name": "key", "value": "{field}" }] |
refreshOnChange | boolean | Re-fetch when any dependency changes |
refreshPaths | string[] | Re-fetch only when these fields change |
Canonical type string list
text | textarea | number | slider | phoneInput | fileUpload | button | numberStepper | signaturePad
select | multiSelect | radio | checkbox | singleCheckbox | toggleSwitch | toggleButton | autocomplete | selectButton | listBox
datepicker | dateRange | timePicker
panel | objectPanel | dynamicPanel | tabs | tabsPro | accordion | dialog | splitter | progressFlow | emptyBlock
dynamicTable | table | listGrid | chart
richText | richTextViewer | customHtml | htmlSnippet | image | video | iframe | avatar | icon | routerOutlet
divider | spacer | breadcrumbs | pageTitle | badge | messageCard | statsCard | toast | progressBar
pageType strings are exact and case-sensitive. Never alter capitalization.