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:
- Design-time: where the JSON Schema is built using the Form Designer or manually
- 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 Type | Purpose | Examples |
---|---|---|
Property Widget | Binds to data (core or chars) | tsm-text, tsm-datepicker |
Layout Widget | Defines structure/position/UX | dtl-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
- Form schema loaded from backen
- Default values injected
- JEXL expressions parsed and applied
- Data bindings resolved
- Widgets instantiated and rendered (lazily)
- 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.