Events Manager
Overview
In Servoy, developers have two complementary methods for handling events within their applications: Direct Event Binding and the Events Manager. Each method serves specific scenarios and use cases, ensuring developers have the flexibility to choose the approach most suited to their needs.
Direct Event Binding refers to associating one event directly with a single handler, typically done at design-time. This is the default and most straightforward approach recommended by Servoy for handling events that have clear, simple actions and outcomes. It's particularly effective when an event triggers a clearly defined response or functionality—such as clicking a button to save a record, or initializing a form when it's displayed. This approach provides simplicity, clarity, and direct visibility of event-handler relationships within the Servoy Developer environment. For detailed information and examples on handling form events using Direct Event Binding, refer to the Form Events documentation.
Events Manager was specifically created to address more advanced, complex event-handling scenarios where Direct Event Binding would be insufficient or less effective. By employing the Observer pattern, the Events Manager enables multiple independent listeners to be registered dynamically to a single event. This capability significantly enhances modularity, maintainability, and flexibility—allowing for centralized event monitoring and handling without tight coupling or breaking encapsulation. For example, when a critical event like a record update occurs, several unrelated components (auditing, logging, notifications) can independently respond to the event without explicitly referencing each other. This design promotes a clean separation of concerns and a modular architecture, essential for larger, sophisticated Servoy applications.
Therefore, while Direct Event Binding remains the recommended and optimal solution for simple, straightforward scenarios due to its simplicity and directness, the Events Manager is essential for developers who encounter more intricate requirements. It allows for flexible and dynamic event handling, facilitates centralized logging and monitoring, and ensures adherence to best practices in encapsulation and modular design patterns.
The Events Manager in Servoy provides a robust and centralized framework for handling events throughout your applications. It enables developers to manage both built-in and custom-defined events seamlessly, resulting in cleaner, modular, and more maintainable code.
Key advantages include:
Centralizing event logic for ease of maintenance
Facilitating clear communication between modules and components
Simplifying the addition, modification, and removal of event listeners
Aggregating results from multiple event listeners
Events Manager: Using Built-in Events

Servoy includes built-in event types, such as form lifecycle events (onShow
, onHide
). The Events Manager streamlines the management of these events across the application.
Step-by-step Guide: Using a Built-in Event
Here are the steps to use Events Manager for Built-In Events:
Choose the Built-in Event: Select the built-in event you wish to handle, for instance,
onShow
, triggered whenever a form is shown.Register an Event Listener: Add your event handler logic centrally using the Events Manager addEventListener method.
Triggering the Event: No explicit triggering is needed for built-in events; Servoy automatically triggers these events at the appropriate times.
Example
Scenario: You have a Navbar component with a search input that should only be enabled when the user clicks the "Customers" menu item and disabled (and cleared) for any other page.
Implementation Steps:
Step 1: Initialize Navbar and Register Event Listeners: In your main form (
main_navbar
), initialize the navbar and search input, then register listeners to the built-inonShow
andonBeforeHide
events specifically of the "Customers" form (customers_navbar
)
Step 2: Define Helper Functions: Define clear helper functions to encapsulate logic for enabling, disabling, and clearing the search input
Step 3: Handle Menu Item Clicks: Ensure proper navigation and search action handling
Step 4: Execute Search Logic: Define search logic based on active form context and input
Here is the code example of the main form:
/**
* @properties={typeid:35,uuid:"7EA94A7F-905C-4017-B4BA-2EDA28F1A1D6",variableType:-4}
*/
var search_var = null;
/**
* @type {JSMenuItem}
*
* @properties={typeid:35,uuid:"39278DF1-15C9-4482-AA1F-9CE6B7554929",variableType:-4}
*/
var menuitemSearch;
/**
* Callback method for when form is shown.
*
* @param {Boolean} firstShow form is shown first time after load
* @param {JSEvent} event the event that triggered the action
*
* @private
*
* @properties={typeid:24,uuid:"48F91A76-86BD-46D1-8D01-F40552278FB7"}
*/
//Initialize Navbar and Register Event Listeners:
function onShow(firstShow, event) {
elements.fc_navbar.containedForm = forms.home_nav;
menuitemSearch = elements.navbar_demo.servoyMenu.getMenuItem("search");
emptySearchValue();
EnableSearchInput(false);
eventsManager.addEventListener(EventType.onShow, EnableSearch, forms.customers_navbar);
eventsManager.addEventListener(EventType.onBeforeHide, DisableAndEmptySearch, forms.customers_navbar);
}
//Define Helper Functions: Define clear helper functions to encapsulate logic for enabling, disabling, and clearing the search input:
/**
* @properties={typeid:24,uuid:"B085C8E9-56B5-4EDD-B2DB-E99EC6FBEB98"}
*/
function emptySearchValue()
{
menuitemSearch.setExtraProperty('Navbar','dataProviderValue','');
}
/**
* TODO generated, please specify type and doc for the params
* @param {Boolean} bool
*
* @properties={typeid:24,uuid:"A90CFF18-D835-4636-A392-10007BB84269"}
*/
function EnableSearchInput(bool)
{
menuitemSearch.enabled = bool;
}
/**
* TODO generated, please specify type and doc for the params
* @param event
* @param arg1
* @param arg2
*
* @properties={typeid:24,uuid:"99AF3180-AE6B-4836-9127-1A7278E99247"}
*/
function EnableSearch(event, arg1, arg2) {
EnableSearchInput(true);
}
/**
* TODO generated, please specify type and doc for the params
* @param event
* @param arg1
* @param arg2
*
* @properties={typeid:24,uuid:"9C7BBEEE-110C-46D9-B863-6786FEFF7DDC"}
*/
function DisableAndEmptySearch(event, arg1, arg2) {
emptySearchValue();
EnableSearchInput(false);
}
/**
* Called whenever a menu item is clicked or a submenu item is selected with the JSEvent and the menuItem object clicked on.
*
* @param {JSEvent} event
* @param {CustomType<bootstrapextracomponents-navbar.menuItem>} menuItem
*
* @private
*
* @properties={typeid:24,uuid:"FE2207A5-B8F5-4EEB-802B-746D9BD9295E"}
*/
//Handle Menu Item Clicks: Ensure proper navigation and search action handling:
function onMenuItemClicked(event, menuItem) {
if(menuItem.itemId != "search" && menuItem.itemId != "contact_name" && menuItem.itemId != "logout")
{
elements.fc_navbar.containedForm = forms[menuItem.itemId];
}
if(menuItem.itemId == "search") {
search_var = menuitemSearch.getExtraProperty('Navbar','dataProviderValue');
searchAction();
}
if(menuItem.itemId == "logout") { application.showForm("navbar_login");}
}
/**
* Called when the user clicks on the brand logo or text.
*
* @param {JSEvent} event
*
* @private
*
* @properties={typeid:24,uuid:"ACC7D45B-EBE1-43E2-AF7F-9EBCC2BFD0B6"}
*/
function onBrandClicked(event) {
elements.fc_navbar.containedForm = forms.home_nav;
}
/**
* @AllowToRunInFind
*
* @properties={typeid:24,uuid:"0143C940-2822-4DA1-A551-DED2451FE825"}
*/
//Execute Search Logic: Define search logic based on active form context and input:
function searchAction() {
var containedForm = elements.fc_navbar.containedForm;
if (containedForm) {
var fs = forms[containedForm].foundset;
if (search_var) {
if (containedForm == "customers_navbar" || containedForm == "employees_navbar") {
if (fs.find()) { // Enter find mode
fs["country"] = search_var + '%';
fs.search(); // Execute the query and load the records
}
}
} else {
fs.loadAllRecords();
}
}
}
Resulting Behavior:
When users click the "Customers" menu item:
Customers form (
customers_navbar
) shows.onShow
built-in event fires.Main form's listener enables the navbar search input, allowing user interaction.
When users click other menu items (e.g., Employees):
Customers form hides (
onBeforeHide
fires).Main form’s listener disables and clears the navbar search input.
This approach ensures dynamic, context-sensitive functionality in your navbar, providing a smooth user experience.
Events Manager: Using Custom Events

Custom events are especially useful for defining application-specific logic that Servoy does not cover through built-in events. This is ideal for business logic, integrations, and custom workflows.
Custom Events Manager in Servoy typically involve scenarios where your application's business logic requires highly customizable and dynamic event handling beyond Servoy's built-in events. Here are several practical examples:
Complex Business Workflows Consider a sales application where completing an order involves multiple subsequent actions. A custom event called
orderCompleted
can be triggered upon successful checkout. Independent listeners could handle tasks such as updating inventory, notifying shipping departments, sending confirmation emails, updating analytics dashboards, and syncing order details with external CRM systems. This ensures each part of your system reacts independently, clearly, and modularly without tight coupling.Centralized Monitoring and Auditing In regulated industries (such as finance, healthcare, or legal sectors), applications often require detailed auditing of key user activities. By defining custom events such as
userLogin
,dataExported
, orsettingsChanged
, you can trigger centralized auditing logic to log critical actions consistently, enabling easier compliance reporting and audit trails.Notifications and Alerts Custom events are ideal for situations where multiple notifications or alerts must be sent based on specific triggers. For example, triggering a custom event
paymentProcessed
can result in separate listeners that notify the finance team, send receipt emails to the customer, and update account balances simultaneously, all decoupled from the main payment-processing logic.Integration and Data Synchronization When integrating with external systems, custom events like
customerDataUpdated
can trigger multiple independent actions—such as syncing customer information to marketing platforms, external analytics services, or accounting systems. Each integration can be managed separately, simplifying maintenance and debugging.Feature Toggles and Dynamic Behavior Custom events support dynamic feature toggles and behavioral flags. For instance, when an administrator toggles a new feature via a dashboard (
featureToggleActivated
event), registered listeners across your app can automatically activate or deactivate UI components, enabling rapid rollout or rollback of application features.
Step-by-step Guide: Using a Custom Event (orderCompleted)
Here are the steps to use Events Manager for Custom Events:
Define a Custom Event Type: Clearly define your event type for readability and maintainability.
In the Solution Explorer, click the solution name (e.g., demo) and open the Properties view.
Under the Properties section, locate the
eventTypes
field. Clicking on the[]
button opens the Event Types Editor.
Register Event Listener(s): Attach your listener(s) to the custom event using the Events Manager.
Trigger Your Custom Event: Manually trigger your custom event whenever appropriate within your business logic.
All registered listeners respond automatically when the event is triggered, ensuring modular and decoupled application design.
Example Use Case: Custom Events in a Multi-Section Order Form
Scenario
In this example, a main Order Entry form contains three form containers with the inner forms:
Customer Info
Ship Info
Order Items
Each section is encapsulated in its own form and has its own save-related logic and validation rules. Rather than writing a monolithic onSave
handler that reaches into each subform, the main form coordinates the flow by firing custom events using the eventsManager
.
This approach implements the Observer pattern: each section listens for relevant events and reacts independently, without breaking encapsulation.
The following custom events are defined in the solution's eventTypes
property:
ON_BEFORE_SAVE
ON_SAVE
ON_SAVE_FAILED
ON_SAVE_REVERTED
Save and Cancel Flow
On Save
button click, the main form executes this logic, firing custom events (ON_BEFORE_SAVE
, ON_SAVE
, ON_SAVE_FAILED
):
function onActionSave(event) {
// Notify listeners; allow them to block save if needed
var continueSave = eventsManager.fireEventListeners(EventType.ON_BEFORE_SAVE, foundset);
if (continueSave) {
databaseManager.saveData();
plugins.webnotificationsToastr.success('Your order was saved', 'Success');
eventsManager.fireEventListeners(EventType.ON_SAVE, foundset);
} else {
plugins.webnotificationsToastr.error('Please check error markers', 'Save Failed');
eventsManager.fireEventListeners(EventType.ON_SAVE_FAILED, foundset);
}
updateUI();
}
On Cancel
, the form reverts data and fires a ON_SAVE_REVERTED
event:
function onActionCancel(event) {
databaseManager.revertEditedRecords();
eventsManager.fireEventListeners(EventType.ON_SAVE_REVERTED, foundset);
updateUI();
}
The updateUI()
method updates button states based on the current form edit status.
fireEventListeners
method is used to fire a custom event.
Inner Forms: Listening to Events
Each subform (e.g., Ship Info
) registers its listeners during onShow
and removes them on onHide
:
function onShow(firstShow, event) {
eventsManager.addEventListener(EventType.ON_BEFORE_SAVE, onBeforeSave, foundset);
eventsManager.addEventListener(EventType.ON_SAVE_FAILED, onSaveFailed, foundset);
eventsManager.addEventListener(EventType.ON_SAVE, onSave, foundset);
eventsManager.addEventListener(EventType.ON_SAVE_REVERTED, onSaveReverted, foundset);
}
function onHide(event) {
eventsManager.removeEventListener(EventType.ON_BEFORE_SAVE, onBeforeSave, foundset);
eventsManager.removeEventListener(EventType.ON_SAVE_FAILED, onSaveFailed, foundset);
eventsManager.removeEventListener(EventType.ON_SAVE, onSave, foundset);
eventsManager.removeEventListener(EventType.ON_SAVE_REVERTED, onSaveReverted, foundset);
return true;
}
addEventListener
method is used to add a custom event listener.
removeEventListener
method is used to remove a custom event listener.
Each form defines its own handler logic. For example, in Ship Info
, the onBeforeSave
validates that the shipping date isn't before the order date:
function onBeforeSave(event) {
clearMarkers();
if (shippeddate < orderdate) {
elements.shippeddate.toolTipText = 'Shipped date cannot be before order date';
elements.shippeddate.addStyleClass('field-invalid');
return false;
}
return true;
}
function clearMarkers() {
elements.shippeddate.toolTipText = 'Ship Date';
elements.shippeddate.removeStyleClass('field-invalid');
}
Result
As presented in the demo:
Clicking Save triggers all inner form validations (
ON_BEFORE_SAVE
).If any listener returns
false
, the save is canceled andON_SAVE_FAILED
is fired.If all validations pass,
databaseManager.saveData()
is called andON_SAVE
is fired.Clicking Cancel reverts all changes and triggers
ON_SAVE_REVERTED
.
This use case highlights the strength of the Events Manager for orchestrating modular logic across multiple forms without hard dependencies—keeping each form self-contained while still participating in a coordinated save process.
Last updated
Was this helpful?