Form View Events
Overview
Form view function extensions let you react to lifecycle events in a SolidX form. Use them to:
- Read or mutate form data (via Formik)
- Adjust the layout dynamically (hide/show/move fields) with
SolidViewLayoutManager - Coordinate with custom widgets rendered in the form (see Form View Field Widgets)
You author listener functions, register them with registerExtensionFunction, and reference them in your form view layout JSON.
Supported Events (Current)
SolidX currently supports these form view events:
onFormLoad- unified form-load lifecycle event (recommended for all load-time logic).onFieldChange- fires whenever a field value changes.onFieldBlur- fires when a field loses focus.
If your handler mutates layout and/or data, return the appropriate flags:
layoutChanged: true and/or dataChanged: true. Without these flags, changes are ignored.
Form Load Execution Behavior
Inside SolidFormView, form-load handlers run with a working layout/data context and are committed once at the end.
Current execution sequence:
onFormLayoutLoad(legacy compatibility hook)onFormDataLoad(legacy compatibility hook)onFormLoad(recommended unified hook)- Commit final
workingLayoutandworkingFormDatato React state
This means onFormLoad sees the latest working layout/data and is the best place for load-time logic moving forward.
Project Structure & File Paths
Form Event Listeners
- Scope: form-view event listeners for a specific
<module, model>pair. - Mandatory listener location:
solid-ui/src/extensions/<module-name>/<model-name>/form-event-listeners/
- Do not place form-event listeners in generic folders when module/model is known.
Default file selection for form event changes:
- Create or update a TS/TSX listener inside:
solid-ui/src/extensions/<module-name>/<model-name>/form-event-listeners/
- Register the listener in:
solid-ui/src/extensions/solid-extensions.ts
- Keep registration key aligned with layout metadata event references (
onFormLoad,onFieldChange,onFieldBlur).
Example structure:
solid-ui/src/extensions/
├── solid-extensions.ts
└── library/
└── book/
└── form-event-listeners/
└── bookFormViewChangeHandler.ts
Creating a Handler
Here is a concise example that:
- Handles
onFormLoadfor load-time setup - Handles
onFieldChangefor reactive logic - Uses one function for all form events
import { SolidViewLayoutManager } from "@solidxai/core-ui";
const handleBookFormViewChange = (event: SolidUiEvent) => {
const { type, modifiedField, modifiedFieldValue, formData, viewMetadata } = event;
const layoutManager = new SolidViewLayoutManager(viewMetadata.layout);
// Preferred load-time hook
if (type === "onFormLoad") {
layoutManager.updateNodeAttributes("book-advanced-section", { visible: false });
return {
layoutChanged: true,
newLayout: layoutManager.getLayout(),
};
}
// Field change example
if (type === "onFieldChange" && modifiedField === "title") {
layoutManager.updateNodeAttributes("title-caption-node", { visible: true });
return {
layoutChanged: true,
dataChanged: true,
newFormData: {
...formData,
ctxtTitleAlphabetCount: modifiedFieldValue ? String(modifiedFieldValue).length : 0,
},
newLayout: layoutManager.getLayout(),
};
}
};
export default handleBookFormViewChange;
If you set dataChanged: true, return the full newFormData object, not just the modified field.
Keep model concerns together. Use a single file (for example bookFormViewChangeHandler.ts) for form-view event logic for that model.
Registering the Handler
Register each exported function with an alias in solid-extensions.ts:
import handleBookFormViewChange from "./library/book/form-event-listeners/bookFormViewChangeHandler";
import { registerExtensionFunction } from "@solidxai/core-ui";
registerExtensionFunction("bookFormViewChangeHandler", handleBookFormViewChange);
Using Handlers in Layout Metadata
Reference your handler alias in the layout JSON:
{
"name": "book-form-view",
"layout": {
"type": "form",
// Preferred load hook:
"onFormLoad": "bookFormViewChangeHandler",
// Field-level hooks:
"onFieldChange": "bookFormViewChangeHandler",
"onFieldBlur": "bookFormViewChangeHandler"
}
}
Your handler can be one function that switches on event.type, or multiple functions registered under different aliases.
Event Payload (Types)
Handlers receive a SolidUiEvent payload.
export type SolidUiEvent = {
type: SolidUiEvents; // e.g., "onFormLoad", "onFieldChange"
modifiedField?: string;
modifiedFieldValue?: any;
queryParams?: any;
formData: Record<string, any>; // current Formik values
viewMetadata: SolidView; // includes the current layout
fieldsMetadata: FieldsMetadata; // field definitions & constraints
formViewLayout: LayoutNode; // current working layout
};
Returning Changes
Your handler may optionally return an object to apply mutations:
return {
layoutChanged: boolean,
dataChanged: boolean,
newFormData?: Record<string, any>,
newLayout?: LayoutNode
};
- If you touch layout, set
layoutChanged: trueand returnnewLayout. - If you touch data, set
dataChanged: trueand returnnewFormData. - If you touch both, set both flags and return both payloads.
Deprecated Load Events (Compatibility Only)
onFormLayoutLoad and onFormDataLoad are deprecated for new development.
Prefer onFormLoad as the single load lifecycle hook.
These legacy hooks are still executed for backward compatibility, but new implementations should migrate to onFormLoad.
Legacy metadata pattern:
{
"layout": {
"onFormLayoutLoad": "bookFormViewChangeHandler",
"onFormDataLoad": "bookFormViewChangeHandler"
}
}
Recommended metadata pattern:
{
"layout": {
"onFormLoad": "bookFormViewChangeHandler"
}
}
Migration guidance:
- Move load-time layout logic from
onFormLayoutLoadintoonFormLoad. - Move load-time data shaping logic from
onFormDataLoadintoonFormLoad. - Keep
onFieldChangeandonFieldBlurunchanged. - Remove deprecated hooks from metadata once migration is complete.
Common Patterns
- Conditional sections: During
onFormLoad, hide/show layout nodes based on defaults, permissions, or query context. - Derived fields: On
onFieldChange, compute dependent values and returnnewFormData. - Async validations: Use
onFieldBlurfor lightweight checks. For heavy validation, prefer server-side logic. - Safety first: Avoid deep mutation of layout objects directly; use
SolidViewLayoutManager.
Troubleshooting
- My changes are not applied -> Verify
layoutChanged/dataChangedflags and corresponding payloads are returned. - Load-time logic is not running -> Ensure the layout has
onFormLoadand the alias matchesregisterExtensionFunction. - Node not found -> Verify the node ID passed to
updateNodeAttributesexists in the layout tree. - Nothing happens on change -> Confirm
onFieldChangeis mapped and handler checks the correct field name.
See Also
- Form View Field Widgets - build custom view/edit widgets and reference them via
viewWidget/editWidget. - Module Metadata Schema - details on form-view layout JSON and node attributes.