Skip to main content

Form Rendering Architecture

This document outlines the rendering logic, widget roles, and runtime considerations of the tSM Form Engine. It is meant for advanced users troubleshooting form behaviors, optimizing layout performance, or designing deeply nested UIs.

Design vs Runtime Architecture

In tSM, every form exists in two phases:

  1. Design-time: where the JSON Schema is built using the Form Designer or manually
  2. Runtime: where the schema is interpreted and rendered dynamically based on the current context, data, and permissions

Widgets: Property vs Layout

Widgets are divided into two fundamental types:

Widget TypePurposeExamples
Property WidgetBinds to data (core or chars)tsm-text, tsm-datepicker
Layout WidgetDefines structure/position/UXdtl-fluent-card, columns

Special cases:

  • Characteristic field: Layout Widget at design-time (placeholder), becomes Property Widget at runtime (binds to chars)
  • TsmControl field (core attribute): Layout Widget in design, but persists to core entity attributes at runtime

Lazy Loading of Widgets

All widgets in tSM are lazily initialized:

  • They are rendered only when needed (e.g. when in viewport or expanded)
  • Reduces time-to-interactive for complex forms

Use collapsible sections, tabs, and dialogs to improve perceived performance on large schemas

Layout Composition & Nesting

Layouts can be deeply nested but should remain manageable. Common performance-friendly patterns:

  • Card → Columns → Fields
  • Tab → Card → Section → Fields
  • Avoid: Tabs inside cards inside tabs recursively

Use config.collapsible, hidden, and readonly with JEXL expressions to conditionally simplify runtime UI.

Dynamic Evaluation

At runtime, the following are evaluated for every widget:

  • JEXL expressions for visibility, default, readonly, disabled, etc.
  • Data binding: form.chars, form.entity, form.related.*
  • Access control (based on user role)
  • Widget-level config (e.g. minLength, validations)

These are performed client-side using the $context object (in frontend) or via SpEL on the backend.

Rendering Lifecycle

  1. Form schema loaded from backen
  2. Default values injected
  3. JEXL expressions parsed and applied
  4. Data bindings resolved
  5. Widgets instantiated and rendered (lazily)
  6. Listings and LOVs queried if configured

Note: invalid JEXL or unresolved bindings may cause rendering issues. Always validate complex forms with the Debug Console.

Debugging Tools

  • Form Debug Console: inspect current $context, live bindings, and expression results
  • JEXL Watcher: track expression re-evaluations
  • Dev Tools: inspect lazy component trees and API calls

Summary Recommendations

  • Avoid over-nesting layout containers
  • Group rarely used fields in collapsible cards or tabs
  • Use visibility logic to control complexity dynamically
  • Enable pagination in Listings
  • Validate expressions early (especially in Production)

Understanding the runtime architecture of form rendering helps avoid regressions, boost performance, and build more maintainable layouts.