Field Change Events
Field change events trigger when a user modifies field values. Use these to calculate dependent values, validate input, or load related data.
Event Types
Events: onChange, onBlur, onFocus
| Event | Triggered When |
|---|---|
onChange | Field value changes |
onBlur | Field loses focus |
onFocus | Field receives focus |
Request Example
{
"widgetName": "quantity",
"widgetEvent": "onChange",
"widgetValue": "5",
"formData": {
"quantity": "5",
"unitPrice": "100.00",
"totalPrice": "0"
},
"formCode": "ORDERFORM",
"guid": "new"
}
Common Use Cases
Calculate Dependent Fields
- C# / .NET
- PHP
if (widgetName == "QUANTITY" && eventType == "ONCHANGE")
{
// Calculate total price when quantity changes
if (decimal.TryParse(formData.GetValueOrDefault("quantity", "0").ToString(), out var quantity) &&
decimal.TryParse(formData.GetValueOrDefault("unitPrice", "0").ToString(), out var unitPrice))
{
var totalPrice = quantity * unitPrice;
formData["totalPrice"] = totalPrice.ToString("F2");
response["formData"] = formData;
}
}
else if (widgetName == "UNITPRICE" && eventType == "ONCHANGE")
{
// Calculate total price when unit price changes
if (decimal.TryParse(formData.GetValueOrDefault("quantity", "0").ToString(), out var quantity) &&
decimal.TryParse(formData.GetValueOrDefault("unitPrice", "0").ToString(), out var unitPrice))
{
var totalPrice = quantity * unitPrice;
formData["totalPrice"] = totalPrice.ToString("F2");
response["formData"] = formData;
}
}
if ($widgetName === 'QUANTITY' && $eventType === 'ONCHANGE') {
// Calculate total price when quantity changes
$quantity = floatval($formData['quantity'] ?? 0);
$unitPrice = floatval($formData['unitPrice'] ?? 0);
$totalPrice = $quantity * $unitPrice;
$formData['totalPrice'] = number_format($totalPrice, 2, '.', '');
$response['formData'] = $formData;
}
elseif ($widgetName === 'UNITPRICE' && $eventType === 'ONCHANGE') {
// Calculate total price when unit price changes
$quantity = floatval($formData['quantity'] ?? 0);
$unitPrice = floatval($formData['unitPrice'] ?? 0);
$totalPrice = $quantity * $unitPrice;
$formData['totalPrice'] = number_format($totalPrice, 2, '.', '');
$response['formData'] = $formData;
}
Load Related Data
- C# / .NET
- PHP
if (widgetName == "CUSTOMERID" && eventType == "ONCHANGE")
{
// Load customer details when customer is selected
var customerId = formData.GetValueOrDefault("customerId", "").ToString();
if (!string.IsNullOrEmpty(customerId))
{
var customer = await _dbContext.Customers
.FirstOrDefaultAsync(c => c.Id.ToString() == customerId);
if (customer != null)
{
// Prefill customer details
formData["customerName"] = customer.Name;
formData["email"] = customer.Email;
formData["phone"] = customer.Phone;
formData["address"] = customer.Address;
response["formData"] = formData;
// Load customer's orders in a datatable
var orders = await _dbContext.Orders
.Where(o => o.CustomerId.ToString() == customerId)
.Select(o => new Dictionary<string, string>
{
{ "_id", o.Id.ToString() },
{ "_sk", o.Id.ToString() },
{ "_code", "ORDER" },
{ "orderNumber", o.OrderNumber },
{ "orderDate", o.OrderDate.ToString("yyyy-MM-dd") },
{ "total", o.Total.ToString("F2") }
})
.ToListAsync();
response["widgetData"] = orders;
}
}
}
if ($widgetName === 'CUSTOMERID' && $eventType === 'ONCHANGE') {
// Load customer details when customer is selected
$customerId = $formData['customerId'] ?? '';
if (!empty($customerId)) {
$customer = DB::table('customers')
->where('id', $customerId)
->first();
if ($customer) {
// Prefill customer details
$formData['customerName'] = $customer->name;
$formData['email'] = $customer->email;
$formData['phone'] = $customer->phone;
$formData['address'] = $customer->address;
$response['formData'] = $formData;
// Load customer's orders in a datatable
$orders = DB::table('orders')
->where('customer_id', $customerId)
->get();
$orderData = [];
foreach ($orders as $order) {
$orderData[] = [
'_id' => $order->id,
'_sk' => $order->id,
'_code' => 'ORDER',
'orderNumber' => $order->order_number,
'orderDate' => $order->order_date,
'total' => number_format($order->total, 2, '.', '')
];
}
$response['widgetData'] = $orderData;
}
}
}
Validate Input
- C# / .NET
- PHP
if (widgetName == "EMAIL" && eventType == "ONBLUR")
{
var feCommands = (List<object>)response["feCommand"];
var email = formData.GetValueOrDefault("email", "").ToString();
// Validate email format
if (!string.IsNullOrEmpty(email) && !IsValidEmail(email))
{
feCommands.Add(new
{
command = "ShowMessage",
args = new
{
type = "error",
message = "Please enter a valid email address"
}
});
// Clear invalid email
formData["email"] = "";
response["formData"] = formData;
}
}
// Helper method
private bool IsValidEmail(string email)
{
try
{
var addr = new System.Net.Mail.MailAddress(email);
return addr.Address == email;
}
catch
{
return false;
}
}
if ($widgetName === 'EMAIL' && $eventType === 'ONBLUR') {
$email = $formData['email'] ?? '';
// Validate email format
if (!empty($email) && !filter_var($email, FILTER_VALIDATE_EMAIL)) {
$response['feCommand'][] = [
'command' => 'ShowMessage',
'args' => [
'type' => 'error',
'message' => 'Please enter a valid email address'
]
];
// Clear invalid email
$formData['email'] = '';
$response['formData'] = $formData;
}
}
Dynamic Dropdown Cascade
- C# / .NET
- PHP
if (widgetName == "COUNTRY" && eventType == "ONCHANGE")
{
// Load cities based on selected country
var countryId = formData.GetValueOrDefault("country", "").ToString();
if (!string.IsNullOrEmpty(countryId))
{
var cities = await _dbContext.Cities
.Where(c => c.CountryId.ToString() == countryId)
.ToDictionaryAsync(
c => c.Id.ToString(),
c => c.Name
);
response["fieldAllowedValues"] = new Dictionary<string, object>
{
{ "city", cities }
};
// Clear previously selected city
formData["city"] = "";
response["formData"] = formData;
}
}
else if (widgetName == "CITY" && eventType == "ONCHANGE")
{
// Load districts based on selected city
var cityId = formData.GetValueOrDefault("city", "").ToString();
if (!string.IsNullOrEmpty(cityId))
{
var districts = await _dbContext.Districts
.Where(d => d.CityId.ToString() == cityId)
.ToDictionaryAsync(
d => d.Id.ToString(),
d => d.Name
);
response["fieldAllowedValues"] = new Dictionary<string, object>
{
{ "district", districts }
};
// Clear previously selected district
formData["district"] = "";
response["formData"] = formData;
}
}
if ($widgetName === 'COUNTRY' && $eventType === 'ONCHANGE') {
// Load cities based on selected country
$countryId = $formData['country'] ?? '';
if (!empty($countryId)) {
$cities = DB::table('cities')
->where('country_id', $countryId)
->pluck('name', 'id')
->toArray();
$response['fieldAllowedValues'] = [
'city' => $cities
];
// Clear previously selected city
$formData['city'] = '';
$response['formData'] = $formData;
}
}
elseif ($widgetName === 'CITY' && $eventType === 'ONCHANGE') {
// Load districts based on selected city
$cityId = $formData['city'] ?? '';
if (!empty($cityId)) {
$districts = DB::table('districts')
->where('city_id', $cityId)
->pluck('name', 'id')
->toArray();
$response['fieldAllowedValues'] = [
'district' => $districts
];
// Clear previously selected district
$formData['district'] = '';
$response['formData'] = $formData;
}
}
Show/Hide Fields Based on Value
- C# / .NET
- PHP
if (widgetName == "CUSTOMERTYPE" && eventType == "ONCHANGE")
{
var customerType = formData.GetValueOrDefault("customerType", "").ToString();
if (customerType == "company")
{
// Show company-specific fields
response["widgetsState"] = new
{
visibility = new Dictionary<string, bool>
{
{ "companyName", true },
{ "taxNumber", true },
{ "vatNumber", true },
{ "firstName", false },
{ "lastName", false }
},
readOnly = new Dictionary<string, bool>()
};
}
else if (customerType == "individual")
{
// Show individual-specific fields
response["widgetsState"] = new
{
visibility = new Dictionary<string, bool>
{
{ "companyName", false },
{ "taxNumber", false },
{ "vatNumber", false },
{ "firstName", true },
{ "lastName", true }
},
readOnly = new Dictionary<string, bool>()
};
}
}
if ($widgetName === 'CUSTOMERTYPE' && $eventType === 'ONCHANGE') {
$customerType = $formData['customerType'] ?? '';
if ($customerType === 'company') {
// Show company-specific fields
$response['widgetsState'] = [
'visibility' => [
'companyName' => true,
'taxNumber' => true,
'vatNumber' => true,
'firstName' => false,
'lastName' => false
],
'readOnly' => []
];
}
elseif ($customerType === 'individual') {
// Show individual-specific fields
$response['widgetsState'] = [
'visibility' => [
'companyName' => false,
'taxNumber' => false,
'vatNumber' => false,
'firstName' => true,
'lastName' => true
],
'readOnly' => []
];
}
}
Apply Discount Based on Input
- C# / .NET
- PHP
if (widgetName == "DISCOUNTCODE" && eventType == "ONCHANGE")
{
var feCommands = (List<object>)response["feCommand"];
var discountCode = formData.GetValueOrDefault("discountCode", "").ToString();
// Validate discount code
var discount = await _dbContext.DiscountCodes
.FirstOrDefaultAsync(d => d.Code == discountCode && d.IsActive);
if (discount != null)
{
// Apply discount
formData["discountPercent"] = discount.Percentage.ToString();
// Recalculate total
if (decimal.TryParse(formData.GetValueOrDefault("subtotal", "0").ToString(), out var subtotal))
{
var discountAmount = subtotal * (discount.Percentage / 100m);
var total = subtotal - discountAmount;
formData["discountAmount"] = discountAmount.ToString("F2");
formData["total"] = total.ToString("F2");
}
response["formData"] = formData;
feCommands.Add(new
{
command = "ShowMessage",
args = new
{
type = "success",
message = $"Discount code applied: {discount.Percentage}% off"
}
});
}
else if (!string.IsNullOrEmpty(discountCode))
{
feCommands.Add(new
{
command = "ShowMessage",
args = new
{
type = "error",
message = "Invalid discount code"
}
});
formData["discountCode"] = "";
formData["discountPercent"] = "0";
formData["discountAmount"] = "0";
response["formData"] = formData;
}
}
if ($widgetName === 'DISCOUNTCODE' && $eventType === 'ONCHANGE') {
$discountCode = $formData['discountCode'] ?? '';
// Validate discount code
$discount = DB::table('discount_codes')
->where('code', $discountCode)
->where('is_active', true)
->first();
if ($discount) {
// Apply discount
$formData['discountPercent'] = $discount->percentage;
// Recalculate total
$subtotal = floatval($formData['subtotal'] ?? 0);
$discountAmount = $subtotal * ($discount->percentage / 100);
$total = $subtotal - $discountAmount;
$formData['discountAmount'] = number_format($discountAmount, 2, '.', '');
$formData['total'] = number_format($total, 2, '.', '');
$response['formData'] = $formData;
$response['feCommand'][] = [
'command' => 'ShowMessage',
'args' => [
'type' => 'success',
'message' => "Discount code applied: {$discount->percentage}% off"
]
];
}
elseif (!empty($discountCode)) {
$response['feCommand'][] = [
'command' => 'ShowMessage',
'args' => [
'type' => 'error',
'message' => 'Invalid discount code'
]
];
$formData['discountCode'] = '';
$formData['discountPercent'] = '0';
$formData['discountAmount'] = '0';
$response['formData'] = $formData;
}
}
Best Practices
- Debounce expensive operations - For operations like database lookups, consider implementing debouncing on the frontend
- Validate on blur - Use
onBlurfor validation to avoid interrupting the user while typing - Provide immediate feedback - Use
onChangefor real-time calculations - Clear dependent fields - When a parent field changes, clear dependent child fields
- Handle empty values - Always check for null/empty values before processing
Next Steps
- Button Events - Handle button clicks
- Datatable Events - Handle table interactions
- Frontend Commands - Learn all available commands