RunEvent Basic Concepts
Understanding the request and response structure is essential for implementing your runEvent handler.
Request Structure
When a form event occurs, your runEvent endpoint receives a JSON payload with the following structure:
{
"widgetName": "submitbtn",
"widgetEvent": "onClick",
"formData": {
"customerName": "John Doe",
"email": "john@example.com",
"phone": "+1234567890"
},
"widgetContext": "{\"additionalData\":\"value\"}",
"formCode": "CUSTOMERFORM",
"guid": "abc123-def456-ghi789",
"pluginCode": "NONE",
"projectGuid": "proj-123"
}
Request Fields
| Field | Type | Description |
|---|---|---|
widgetName | string | Name of the widget that triggered the event (lowercase) |
widgetEvent | string | Type of event (onClick, onChange, onTableLoadData, etc.) |
formData | object | Dictionary of all current form field values |
widgetValue | string | Widget-specific value (optional, used for some events) |
widgetContext | string | Additional context data as JSON string |
formCode | string | Code of the form being displayed |
guid | string | Record UUID/GUID or "new" for new records |
pluginCode | string | Plugin code (usually "NONE") |
projectGuid | string | Project identifier |
Special Fields for Datatable Events
For datatable events, additional fields may be present:
{
"DataTableMeta": {
"serverPaginationEnabled": true,
"rowsPerPage": 50,
"pageIndex": 0,
"nextToken": "",
"previousToken": "",
"paginationDirection": "first",
"rowCount": 0,
"qId": ""
}
}
Response Structure
Your runEvent endpoint must return a plain dictionary/object with these fields. You don't need any special classes - just return a JSON response:
{
"formData": { /* Dictionary of form field values */ },
"widgetData": [ /* Array of data for table widgets */ ],
"widgetsState": {
"visibility": { /* Widget visibility states */ },
"readOnly": { /* Widget read-only states */ }
},
"fieldAllowedValues": { /* Dropdown/select options */ },
"feCommand": [ /* Array of commands to execute */ ]
}
Response Fields Explained
| Field | Type | Description |
|---|---|---|
formData | Dictionary | Current values of all form fields. Update field values by modifying this object. |
widgetData | Array | Data for table widgets. Each item is a row with column values. Must include _id, _sk, _code fields. |
widgetsState | Object | Controls widget visibility and read-only state. Contains visibility and readOnly dictionaries. |
fieldAllowedValues | Dictionary | Dropdown/select options. Key is field name, value is dictionary of option key-value pairs. |
feCommand | Array | Commands to execute on frontend (OpenRecord, CloseForm, Download, etc.). |
tableMeta | Object | Pagination metadata for datatable widgets (optional, only for table events). |
widgetRelatedData | Object | Additional widget-related data like selected row IDs (optional). |
You can use plain dictionaries/objects in your response - no special classes needed! The examples below show how to build the response using standard language features.
Response Examples
Update Form Field Values
- C# / .NET
- PHP
// Prefill form fields with data
formData["customerName"] = "John Doe";
formData["email"] = "john@example.com";
formData["phone"] = "+1234567890";
formData["address"] = "123 Main St";
response["formData"] = formData;
// Prefill form fields with data
$formData['customerName'] = 'John Doe';
$formData['email'] = 'john@example.com';
$formData['phone'] = '+1234567890';
$formData['address'] = '123 Main St';
$response['formData'] = $formData;
Control Widget Visibility
- C# / .NET
- PHP
// Hide specific widgets
response["widgetsState"] = new
{
visibility = new Dictionary<string, bool>
{
{ "phone", false },
{ "email", false },
{ "address", false }
},
readOnly = new Dictionary<string, bool>()
};
// Hide specific widgets
$response['widgetsState'] = [
'visibility' => [
'phone' => false,
'email' => false,
'address' => false
],
'readOnly' => []
];
Control Widget Read-Only State
- C# / .NET
- PHP
// Make fields read-only
response["widgetsState"] = new
{
visibility = new Dictionary<string, bool>(),
readOnly = new Dictionary<string, bool>
{
{ "customerName", true },
{ "email", true }
}
};
// Make fields read-only
$response['widgetsState'] = [
'visibility' => [],
'readOnly' => [
'customerName' => true,
'email' => true
]
];
Populate Dropdown Options
- C# / .NET
- PHP
// Populate dropdown with options
response["fieldAllowedValues"] = new Dictionary<string, object>
{
{
"statusField", new Dictionary<string, string>
{
{ "1", "New" },
{ "2", "In Progress" },
{ "3", "Completed" },
{ "4", "On Hold" }
}
},
{
"priorityField", new Dictionary<string, string>
{
{ "low", "Low Priority" },
{ "medium", "Medium Priority" },
{ "high", "High Priority" }
}
}
};
// Populate dropdown with options
$response['fieldAllowedValues'] = [
'statusField' => [
'1' => 'New',
'2' => 'In Progress',
'3' => 'Completed',
'4' => 'On Hold'
],
'priorityField' => [
'low' => 'Low Priority',
'medium' => 'Medium Priority',
'high' => 'High Priority'
]
];
Load Table Widget Data
- C# / .NET
- PHP
// Load data for a table widget
response["widgetData"] = new List<Dictionary<string, string>>
{
new Dictionary<string, string>
{
{ "_id", "customer-001" },
{ "_sk", "customer-001" },
{ "_code", "CUSTOMER" },
{ "name", "John Doe" },
{ "email", "john@example.com" },
{ "status", "Active" }
},
new Dictionary<string, string>
{
{ "_id", "customer-002" },
{ "_sk", "customer-002" },
{ "_code", "CUSTOMER" },
{ "name", "Jane Smith" },
{ "email", "jane@example.com" },
{ "status", "Pending" }
}
};
// Load data for a table widget
$response['widgetData'] = [
[
'_id' => 'customer-001',
'_sk' => 'customer-001',
'_code' => 'CUSTOMER',
'name' => 'John Doe',
'email' => 'john@example.com',
'status' => 'Active'
],
[
'_id' => 'customer-002',
'_sk' => 'customer-002',
'_code' => 'CUSTOMER',
'name' => 'Jane Smith',
'email' => 'jane@example.com',
'status' => 'Pending'
]
];
Table rows must include these fields:
_id- Unique identifier for the row_sk- Sort key (usually same as _id)_code- Form code for the record type
Without these fields, row actions (edit, delete, etc.) will not work properly.
Asynchronous Event Handling
When a form loads, multiple widgets may trigger their event handlers simultaneously. Your backend must be prepared to handle concurrent requests.
Example Scenario
User opens a form with:
- 2 datatable widgets
- 1 dropdown with dynamic options
- 1 onLoad event
Your runEvent endpoint receives 4 simultaneous requests:
1. onTableLoadData (table1)
2. onTableLoadData (table2)
3. onLoad (form)
4. onChange (dropdown)
Best Practices
- Make handlers stateless - Don't rely on request order
- Use proper concurrency handling - Ensure database operations are thread-safe
- Return consistent responses - Each request should get a complete response
- Optimize queries - Concurrent requests should not cause performance issues
Next Steps
- Button Events - Handle button clicks
- Field Events - Handle field changes
- Datatable Events - Handle table interactions
- Frontend Commands - Control the frontend