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
SolidX currently supports these form view events:
onFormLayoutLoad– fires when the layout is loaded. Great for conditional visibility and structure tweaks.onFormDataLoad– fires when data is loaded (edit mode fetch, or defaults for create). Perfect for setting derived values.onFieldChange– fires whenever a field value changes. Ideal for cascading logic and live calculations.onFieldBlur– fires when a field loses focus. Good for validations or light reflows.onCustomWidgetRender– fires when a custom widget is rendered. Useful for last‑mile UI polish.
If your handler mutates layout and/or data, return the appropriate flags: layoutChanged: true and/or dataChanged: true. Without these flags, changes are ignored.
Project Structure & File Paths
Place your handler(s) under your admin extensions folder. A common convention is one handler file per model.
/solid-ui/app/admin/extensions/bookFormViewChangeHandler.ts
/solid-ui/app/admin/extensions/solid-extensions.ts # registration
Creating a Handler
Here’s a concise example that:
- Listens to any form event
- On
onFieldChangeoftitle, shows a hidden node and injects a character count into form data
bookFormViewChangeHandler.ts
import { SolidViewLayoutManager } from "@solidxai/core-ui";
const handleBookFormViewChange = (event: SolidUiEvent) => {
const { type, modifiedField, modifiedFieldValue, formData, viewMetadata } = event;
// Work with the *current* layout
const layoutManager = new SolidViewLayoutManager(viewMetadata.layout);
if (type === "onFieldChange" && modifiedField === "title") {
const title = modifiedFieldValue;
// Reveal a UI node (e.g., a caption area) by nodeId configured in your layout
layoutManager.updateNodeAttributes("page-1-row-1-div-1-div-1-title-custom", { visible: true });
return {
layoutChanged: true, // you changed layout; tell SolidX
dataChanged: true, // you changed data; tell SolidX
newFormData: {
...formData,
// Below is the new field we are injecting, which can be accessed in a custom widget
ctxtTitleAlphabetCount: title ? String(title).length : 0,
},
newLayout: layoutManager.getLayout() // hand back the updated layout
};
}
// For events you don't handle, return nothing.
// You can also return { layoutChanged: false, dataChanged: false } explicitly.
};
export default handleBookFormViewChange;
If you are setting dataChanged to true, ensure you return the full newFormData object, not just the modified field.
Keep model concerns together. Use a single file (e.g., bookFormViewChangeHandler.ts) for all form‑view event logic for that model. It makes maintenance and onboarding much easier.
Registering the Handler
Register each exported function with an alias in solid-extensions.ts:
solid-extensions.ts
import handleBookFormViewChange from "./bookFormViewChangeHandler";
import { registerExtensionFunction } from "@solidxai/core-ui";
registerExtensionFunction("bookFormViewChangeHandler", handleBookFormViewChange);
// Add more registrations as needed…
Using Handlers in a Layout
Reference your handler aliases in the layout JSON for the form view.
module-metadata/<module-name>/<module-name>-metadata.json
{
"name": "book-form-view",
"layout": {
"type": "form",
// Lifecycle hooks:
"onFormLayoutLoad": "bookFormViewChangeHandler",
"onFormDataLoad": "bookFormViewChangeHandler",
// Field-level hooks:
"onFieldChange": "bookFormViewChangeHandler",
"onFieldBlur": "bookFormViewChangeHandler",
// Widget render hook:
"onCustomWidgetRender": "bookFormViewChangeHandler"
// ...rest of your layout tree (children, rows, fields, etc.)
}
}
Your handler can be one function that switches on event.type, or multiple functions registered under different aliases. Choose whichever keeps the code clearer for your team.
Event Payload (Types)
Handlers receive a SolidUiEvent payload.
SolidUiEvent
export type SolidUiEvent = {
type: SolidUiEvents; // e.g., "onFieldChange"
modifiedField?: string; // e.g., "title"
modifiedFieldValue?: any; // e.g., "The Pragmatic Programmer"
formData: Record<string, any>; // current Formik values
viewMetadata: SolidView; // includes the current layout
fieldsMetadata: FieldsMetadata; // field definitions & constraints
formViewLayout: LayoutNode; // shorthand to the current layout node
};
Returning Changes
Your handler may optionally return an object to apply mutations:
return {
layoutChanged: boolean, // did you alter layout?
dataChanged: boolean, // did you alter form data?
// if dataChanged is true, provide newFormData
// The new form data can now be accessed by both In-built and Custom Widgets
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.
Common Patterns
- Conditional sections: During
onFormLayoutLoad, hide or show layout nodes based on defaults or permissions. - Derived fields: On
onFieldChange, compute totals, taxes, or dependent values from other fields, then returnnewFormData. - Async validations: Use
onFieldBlurto kick off lightweight checks (e.g., uniqueness). For heavy work, prefer server‑side validation. - Safety first: Avoid deep mutations of
viewMetadata.layoutdirectly; useSolidViewLayoutManager. Always return flags correctly.
Troubleshooting
- “My changes aren’t applied” → Verify you set
layoutChanged/dataChangedappropriately. - On load layout changes not applied → Ensure you have used
onFormLayoutLoadand notonFormDataLoad, if you are trying to change the layout on load. - “Node not found” → Ensure the node ID you pass to
updateNodeAttributesactually exists in your layout tree. - “Nothing happens on change” → Confirm your alias in the layout matches the one passed to
registerExtensionFunction. - “Handlers fight each other” → Centralize model logic in a single file, or partition clearly by event and document the contract.
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.