LearnNewsExamplesServices

Custom Events

As you read in the Getting Started > Events topic, components, stores, and many other objects fire events.

Event listeners

In-line

The event listener function can be coded in-line. Normally you want event handlers to be in a view's controller, but for very simple situation it can be convenient to use this syntax.

As a view method

You can also use the up. qualifier to specify a method in the component's parent view. Like the in-line syntax you saw above, using the up. syntax might be convenient for simple classees, or when you simply haven't gotten around to defining a view's controller.

As a controller method

Despite the examples above, the most correct way of setting up event handlers is to use a controller. Any view class can specify a controller — wWhen the view is created a controller instance is also created.

Adding listeners procedurally

Event listeners are normally specified declarative, via the listeners: {} config. But occasionally you need to add a listener procedurally.

Any observable class has an addListener method, along with an easier-to-type version called on.

The method specified in on() doesn't have to be an arrow function; you can use a controller function.

Events versus binding

Events and binding are similar concepts — both are a way to detect, and react to, some kind of action or change.

Events are fired for user actions, such as a button click event, a component receiving focus, or a field value changing. Non-visual classes can also fire an event; for example, a Neo.data.Store fires a load event, and other events relating to changes to the store. The event handler if a function run when the event fires.

A binding detects a changed view model value, and assigns it to a property.

When to use an event

Events and event handlers are used when you need to run non-trivial logic in response to the event. For example, you might have a Save button, and in its click event handler you'd write logic to make a backend call.

Events can be fired for a state change, such as an input field's value changing, or something like a user event, like a button click or component focus.

When to use a binding

A binding is a way to keep properties in sync with values in the view model hierarchy. For example, button text, field values, or store properties, can simultaneously reflect the same view model property. That's pretty handy, but keep in mind that a class can define a property as a lifecycle property. That means that updating a property can may result in complex logic being triggered. Furthermore, a two way binding means a change to a property will automatically be reflected in the view model.

A simple comparison

To contrast syntax, and to illustrate the simplicity of a binding, let's look at two exmaples of updating a component to reflect the value of a text field. THe first example uses events; the second uses bindings.

How are events set up?

The details don't really matter, but in case you're curious: Neo.mjs has a Neo.core.Observable class that can be mixed into any class. It maintains a listeners object map that's a key-value pair, where the key is the event name, and the value is an array of function references. The first time a listener is added, an entry is added to the map using the event name as the key, and the event handler added as the first item in the associated array. If another listener is added for the same event, a second item is added to the array. If a new event is added, a new entry is added. Etc. When the event is fired, Neo.mjs looks up the map entry for the event name, then runs each function in the array, passing whatever data is specified in the call to fire().