Skip to main content

tSM-Specific Schema Extensions

In addition to standard JSON Schema capabilities, tSM introduces a number of powerful metadata extensions that allow forms to be dynamic, context-aware, and tightly integrated with the backend platform.

This document explores those extensions in depth.

Overview of Extensions

FieldPurpose
widgetDeclares the Angular component to render the field or layout block
configHosts all behavior: UI labels, conditions, defaults, integrations
layoutDefines visual grouping and composition of form elements
readonly, hidden, defaultControl runtime behavior via JEXL
validationMessagesProvide static or dynamic error feedback
dispatchOnChangeTrigger NGRX actions on field updates
selectFromStoreBind widget values to NGRX selectors
dataSource, filters, selectPropertyConfigure LOVs, listings, and data-driven widgets

Detailed Examples

1. widget

Each field (or layout block) must define its widget.type, which maps to a registered Angular component.

Examples:

"widget": { "type": "tsm-text" }
"widget": { "type": "dtl-fluent-tab" }

2. config

A deeply extensible object that controls:

  • Static settings: label, tooltip, legend, columns, etc.
  • Dynamic logic: hidden, readonly, validationMessages (JEXL-based)
  • Data & UX behaviors: lazyLoad, selectFirstValue, cssClasses, collapsible, etc.
  • Event-driven behavior: Hooks such as dispatchOnChange, selectFromStore

Example:

"config": {
  "label": "Priority",
  "readonly": "${$context.user.role !== 'admin'}",
  "validationMessages": { "required": "Please select a priority." }
}

3. layout

A layout block organizes how widgets are presented on screen:

  • Can use widgets like: dtl-fluent-card, dtl-fluent-tab, dtl-fluent-columns
  • Supports nested content blocks
  • Allows dynamic conditional rendering

Example:

"layout": [
  {
    "type": "layout",
    "widget": { "type": "dtl-fluent-card" },
    "config": {
      "header": "Customer Info",
      "collapsible": true
    },
    "items": ["customerName", "segment"]
  }
]

4. validationMessages with Dynamic Rules

You can provide conditionally evaluated validations using JEXL inside a nested custom block.

"validationMessages": {
  "custom": {
    "expression": "${$context.form.customerType == 'VIP' && $value > 50}",
    "message": "VIP customers cannot select a discount above 50%."
  }
}

5. Computed Properties: readonly, hidden, default

These expressions can be pure booleans or JEXL strings:

"readonly": "${$context.user.role !== 'admin'}"
"default": "addTime(now(), 1, 'days')"
"config": {
  "hidden": "${isNullOrEmpty($context.form.customerId)}"
}

6. Event Bindings: dispatchOnChange, selectFromStore

tSM widgets can be bound to state actions and selectors using:

  • dispatchOnChange: Fires an NGRX action on value change
  • selectFromStore: Subscribes to state values and reflects them into widget state

Example:

"config": {
  "dispatchOnChange": {
    "type": "UpdatePriority",
    "payload": { "id": "$context.entity.id", "priority": "$value" }
  },
  "selectFromStore": {
    "storeSelector": "getCurrentPriority",
    "target": "default"
  }
}

7. Data Integration Fields for LOVs and Listings

"config": {
  "dataSource": "ticketStatuses",
  "filters": {
    "statusGroup": "OPEN"
  },
  "selectProperty": "code",
  "lazyLoad": true,
  "selectFirstValue": true
}

These are essential for rendering value lists and inline embedded listings.


8. Advanced Layout Features

  • tabs: Named tab groups
  • columns: Horizontal layouts
  • collapsible: Expand/collapse any card or section
  • visibilityLogic: Controlled via config.hidden
  • readonlyBased storage modes

Summary

tSM's extensions to JSON Schema are designed to provide a fully metadata-driven UI engine that supports:

  • Live logic and scripting (via JEXL)
  • Deep backend integration (via selectors, dispatchers)
  • Layered layout and dynamic control
  • Fine-grained access, visibility, and interaction rules

These fields go beyond simple configuration — they represent the architecture of a low-code platform’s UI layer.