Enhanced Actions
Advanced action system with AJAX calls, chaining, conditions, and callbacks
Enhanced Actions
ObjectUI's action system has been significantly enhanced to support complex workflows, including AJAX API calls, confirmation dialogs, action chaining, conditional execution, and comprehensive tracking.
Overview
The enhanced ActionSchema provides:
- New action types:
ajax,confirm,dialog - Action chaining: Execute multiple actions sequentially or in parallel
- Conditional logic: If/then/else execution based on data
- Callbacks: Success and failure handlers
- Tracking: Event logging and analytics
- Retry logic: Automatic retry with configurable backoff
Interactive Examples
Action Buttons
Action Button Variants
Confirmation Dialog Pattern
Confirmation Dialog
Action Toolbar
Action Toolbar
Action Types
Ajax Actions
Execute API calls with full request configuration:
const ajaxAction: ActionSchema = {
type: 'action',
label: 'Load Data',
actionType: 'ajax',
api: '/api/users',
method: 'GET',
headers: {
'Authorization': 'Bearer token'
},
data: {
filter: 'active'
},
onSuccess: {
type: 'toast',
message: 'Data loaded successfully'
},
onFailure: {
type: 'message',
message: 'Failed to load data'
}
};Ajax Properties:
api- API endpoint URLmethod- HTTP method (GET, POST, PUT, DELETE, PATCH)data- Request body/payloadheaders- Custom HTTP headerstimeout- Request timeout in millisecondsretry- Retry configuration
Confirm Actions
Show confirmation dialog before executing:
const confirmAction: ActionSchema = {
type: 'action',
label: 'Delete Record',
actionType: 'confirm',
confirm: {
title: 'Confirm Deletion',
message: 'Are you sure you want to delete this record?',
confirmText: 'Yes, Delete',
cancelText: 'Cancel',
confirmVariant: 'destructive'
},
api: '/api/records/123',
method: 'DELETE'
};Confirm Properties:
title- Dialog titlemessage- Confirmation messageconfirmText- Confirm button textcancelText- Cancel button textconfirmVariant- Button style
Dialog Actions
Open a modal or dialog:
const dialogAction: ActionSchema = {
type: 'action',
label: 'Edit Details',
actionType: 'dialog',
dialog: {
title: 'Edit Record',
size: 'lg',
content: {
type: 'form',
fields: [
{ name: 'name', type: 'input', label: 'Name' },
{ name: 'email', type: 'input', label: 'Email' }
]
},
actions: [
{
type: 'action',
label: 'Save',
actionType: 'ajax',
api: '/api/records/123',
method: 'PUT'
}
]
}
};Action Params (input collection)
An action may declare params (spec ActionParamSchema) to collect user input
in a dialog before it runs. Each param renders through the same field-widget
renderer the object form uses, so a param of any form-supported field type —
select, lookup, date, file, image, richtext, color, address, … —
gets its real widget, not a text box (ADR-0059):
{
"name": "approve",
"label": "Approve",
"params": [
{ "name": "comment", "type": "textarea", "label": "Comment", "required": true },
{ "name": "attachments", "type": "file", "multiple": true, "accept": ["application/pdf"] },
{ "name": "assignee", "field": "owner_id" },
{ "name": "notify", "type": "boolean", "label": "Notify the requester", "defaultValue": true }
]
}- Inline params declare
name+type(any specFieldType), plus widget config:options,multiple,accept,maxSize,placeholder,helpText,defaultValue. - Field-backed params declare
field(+ optionalobjectOverride) and inherit label, type, options, lookup picker config,multiple,accept, andmaxSizefrom the object's field definition; inline properties override. requiredblocks submit while the value is empty;visible(a CEL predicate overfeatures/current_user/app/data) hides a param entirely — e.g. gate a param on an opt-in server capability.- Values are passed through to the action exactly as the widget emits them
(
number→ number,date→YYYY-MM-DD, lookup → record id(s),file→ uploaded file descriptor(s); arrays whenmultiple).
File/image params upload through the ambient UploadProvider; lookup/user
params query through the surrounding SchemaRendererContext data source — no
extra wiring per action. While a file/image upload is in flight the dialog's
Confirm button is disabled (labelled "Uploading…"), so a param can't be
submitted before its uploaded fileId is ready.
Action Chaining
Execute multiple actions in sequence or parallel:
const chainedAction: ActionSchema = {
type: 'action',
label: 'Process Order',
actionType: 'ajax',
api: '/api/orders/process',
method: 'POST',
chain: [
{
type: 'action',
label: 'Send Email',
actionType: 'ajax',
api: '/api/emails/send',
method: 'POST'
},
{
type: 'action',
label: 'Update Inventory',
actionType: 'ajax',
api: '/api/inventory/update',
method: 'PUT'
},
{
type: 'action',
label: 'Log Event',
actionType: 'ajax',
api: '/api/events/log',
method: 'POST'
}
],
chainMode: 'sequential' // or 'parallel'
};Chain Modes:
sequential- Execute actions one after another (default)parallel- Execute all actions simultaneously
Conditional Execution
Execute different actions based on conditions:
const conditionalAction: ActionSchema = {
type: 'action',
label: 'Approve',
actionType: 'button',
condition: {
expression: '${data.amount > 1000}',
then: {
type: 'action',
label: 'Require Manager Approval',
actionType: 'confirm',
confirm: {
title: 'Manager Approval Required',
message: 'Amount exceeds $1000. Manager approval needed.'
}
},
else: {
type: 'action',
label: 'Auto Approve',
actionType: 'ajax',
api: '/api/approve',
method: 'POST'
}
}
};Condition Properties:
expression- JavaScript expression to evaluatethen- Action(s) to execute if trueelse- Action(s) to execute if false
Callbacks
Handle success and failure scenarios:
const actionWithCallbacks: ActionSchema = {
type: 'action',
label: 'Submit',
actionType: 'ajax',
api: '/api/submit',
method: 'POST',
onSuccess: {
type: 'toast',
message: 'Submitted successfully!'
},
onFailure: {
type: 'dialog',
dialog: {
title: 'Submission Failed',
content: {
type: 'text',
value: 'Please try again or contact support.'
}
}
}
};Callback Types:
toast- Show toast notificationmessage- Show message dialogredirect- Navigate to URLreload- Reload dataajax- Execute another API calldialog- Open dialogcustom- Custom handler
Action Tracking
Track actions for analytics:
const trackedAction: ActionSchema = {
type: 'action',
label: 'Download Report',
actionType: 'ajax',
api: '/api/reports/download',
tracking: {
enabled: true,
event: 'report_downloaded',
metadata: {
reportType: 'sales',
format: 'pdf',
dateRange: '2024-01'
}
}
};Retry Logic
Automatically retry failed requests:
const retryAction: ActionSchema = {
type: 'action',
label: 'Submit',
actionType: 'ajax',
api: '/api/submit',
method: 'POST',
timeout: 30000, // 30 seconds
retry: {
maxAttempts: 3,
delay: 1000 // 1 second between retries
}
};Complete Example
A comprehensive action combining multiple features:
const complexAction: ActionSchema = {
type: 'action',
label: 'Process Order',
icon: 'shopping-cart',
variant: 'default',
// Confirm before processing
actionType: 'confirm',
confirm: {
title: 'Confirm Order',
message: 'Process this order for ${data.customerName}?',
confirmText: 'Process Order',
confirmVariant: 'default'
},
// Conditional logic
condition: {
expression: '${data.totalAmount > 1000}',
then: {
type: 'action',
actionType: 'ajax',
api: '/api/orders/process-premium',
method: 'POST'
},
else: {
type: 'action',
actionType: 'ajax',
api: '/api/orders/process-standard',
method: 'POST'
}
},
// Action chain
chain: [
{
type: 'action',
label: 'Send Confirmation Email',
actionType: 'ajax',
api: '/api/emails/order-confirmation',
method: 'POST',
data: {
orderId: '${data.id}',
customerEmail: '${data.customerEmail}'
}
},
{
type: 'action',
label: 'Update Inventory',
actionType: 'ajax',
api: '/api/inventory/update',
method: 'PUT',
data: {
items: '${data.items}'
}
},
{
type: 'action',
label: 'Create Invoice',
actionType: 'ajax',
api: '/api/invoices/create',
method: 'POST'
}
],
chainMode: 'sequential',
// Callbacks
onSuccess: {
type: 'toast',
message: 'Order processed successfully!'
},
onFailure: {
type: 'dialog',
dialog: {
title: 'Order Processing Failed',
content: {
type: 'text',
value: 'Unable to process order. Please try again.'
}
}
},
// Tracking
tracking: {
enabled: true,
event: 'order_processed',
metadata: {
source: 'web_app',
amount: '${data.totalAmount}'
}
},
// Retry
timeout: 60000,
retry: {
maxAttempts: 3,
delay: 2000
},
// Post-action behavior
reload: true,
close: true,
redirect: '/orders/success'
};Runtime Validation
import { ActionSchema } from '@object-ui/types/zod';
const result = ActionSchema.safeParse(myAction);
if (result.success) {
console.log('Valid action configuration');
} else {
console.error('Validation errors:', result.error);
}Use Cases
Enhanced Actions are ideal for:
- API integration - Connect to backend services and external APIs
- Multi-step processes - Execute complex workflows with multiple stages
- Form submissions - Handle form data with validation and callbacks
- Confirmation dialogs - Add safety checks for critical operations
- Event tracking - Monitor user interactions for analytics
- Batch operations - Process multiple items in sequence or parallel
Best Practices
- Use confirm for destructive actions - Always confirm delete, archive, etc.
- Provide clear feedback - Use callbacks to inform users of success/failure
- Chain related operations - Group logically related API calls
- Track important events - Enable tracking for business-critical actions
- Set appropriate timeouts - Don't let users wait indefinitely
- Retry transient failures - Use retry for network-related errors
- Keep chains short - Long chains can be hard to debug
Related
- Building a CRUD App - CRUD operations with actions
- Form - Form submission actions
- Data Source - API integration