FormActionContext Deep Dive
The FormActionContext is the single argument passed to every action handler. It provides complete access to the form's state, the Atlas Forms API client, navigation, and the completion signalling API. Understanding every field is essential for writing robust handlers.
Complete Interface
interface FormActionContext {
// ── Identity ──────────────────────────────────────────────
formId: string; // Numeric form ID as string (e.g., "13001")
tenantId: string; // Current tenant ID
userId?: string; // Current user ID (if available from auth context)
// ── Form Data ─────────────────────────────────────────────
formValues: Record<string, any>; // Snapshot of ALL current field values
formEngine: FormEngine; // Direct FormEngine access (advanced)
// ── Services ─────────────────────────────────────────────
apiClient: FormDefinitionApiClient; // Atlas Forms API (list/get/create/update forms)
// ── Navigation ────────────────────────────────────────────
navigate: (url: string, options?: { target?: '_self' | '_blank' }) => void;
// ── Completion Signals ────────────────────────────────────
complete: (message?: string) => void; // Signal success
fail: (reason?: string) => void; // Signal failure (shows error)
// ── Validation ────────────────────────────────────────────
validate: () => Promise<boolean>; // Run full form validation
// ── Action Configuration ──────────────────────────────────
config?: Record<string, any>; // handlerConfig from the form schema action
}
Field-by-Field Reference
formValues
A plain object snapshot of all current field values at the moment the action was triggered. Keys are field IDs, values are the current field values. For data-table controls, the value is an array of row objects.
// Example formValues object
{
"company-name": "Acme Corp",
"contact-email": "jane@acme.com",
"invoice-lines": [
{ "description": "Setup fee", "qty": 1, "price": 500 },
{ "description": "Monthly licence", "qty": 12, "price": 99 }
],
"terms-accepted": true
}
formEngine
Direct access to the FormEngine instance. Use this for advanced scenarios: reading error state, programmatically setting field values, or triggering validation for specific fields only.
// Set a field value from within the handler
ctx.formEngine.setFieldValue('status', 'approved');
// Check if a specific field is valid
const emailErrors = ctx.formEngine.getFieldError('contact-email');
if (emailErrors) {
ctx.fail('Please fix the email address first.');
return;
}
apiClient
The same FormDefinitionApiClient instance used by the rest of the application. Useful for loading related forms, checking form metadata, or updating form records during the action.
complete() and fail()
These are the ONLY way to signal the end of your handler. Both exit the loading state on the button.
complete(message?)— Shows a success toast with the optional message, resets button statefail(reason?)— Shows an error toast with the optional reason, resets button state
config
The handlerConfig object from the action schema. Use this to parameterise your handler so the same code can serve multiple use cases:
// Schema
{
"type": "custom",
"config": {
"handlerType": "send-notification",
"handlerConfig": {
"channel": "slack",
"recipient": "#approvals",
"urgency": "high"
}
}
}
// Handler
const handler = async (ctx: FormActionContext) => {
const { channel, recipient, urgency } = ctx.config ?? {};
await notifyService.send({ channel, recipient, urgency, data: ctx.formValues });
ctx.complete('Notification sent');
};