Tabris Decorators Part 4: Event Handling

NOTE: This article is about Tabris 2.x. Some information is outdated when targeting Tabris 3.x.

In this article, we will cover the new event handling API of the Tabris.js framework provided by the tabris-decorators Tabris.js. It is a part of a blog post series dedicated to that extension. Tabris-decorators make developing mobile apps with Tabris.js 2.x and TypeScript more convenient. It features data binding capabilities, enhanced event handling, and dependency injection, all explicitly designed for the Tabris.js mobile framework. To use it the npm module must be installed separately.

It’s recommended to read the previous posts in this series, though you may skip the first one.

In this chapter we take a look at the event handling features. Unlike data binding, event handling is already built into Tabris.js. However, this API was designed with JavaScript in mind and is a bit clumsy to use in TypeScript. With tabris-decorators we offer an abstraction layer that is not only TypeScript optimized, but also adds a lot of convenient features on top of it.

For the most part this improved event system is independent from decorators and TypeScript. For that reason it will (with minor changes) be moved to the tabris core module in Tabris.js 3.0. In fact, it is already available in the beta 1 release. The old event API will still be supported.


We continue with our example component LabeledInput. As of now it consists of a TextView bound to the labelText property of LabeledInput and a TextInput bound do text:

What we are still missing is the application’s ability to react to user inputs.

Introducing @event

With the @event decorator this is just one line:

And now we can do this:

Or this:

How it works

The @event decorator creates a function of the type ChangeListeners and attaches it to the component instance. This function allows registering event listeners in a type-safe way. The type of the event that the listeners are registered for is derived from the name of the property the @event is attached to:

  • onTextChanged registers listeners for textChanged events.
  • onFooChanged registers listeners for fooChanged events.
  • onBar registers listeners for bar events.

The change event itself is triggered by the property setter that the @bind decorator generated. It too derives the matching event name from the name of the property. So to listen to change events of a two-way binding you only need to have the @event decorator on a field with a name and type that match that of the bound property:

Properties decorated with @property also generate change events whenever their value changes. Therefore we could also add the @event for labelText:

Again the even type is determined by the names of the two properties:

Event Triggering

But @event can do more ChangeListeners actually extends the Listeners type, which is not just a function but also an object providing new API to manage listeners and dispatch events. It is an abstraction of the built-in Tabris event system (i.e. on, off and trigger methods) and can be used interchangeably.

Let’s assume another component that has the @event on onFoo:

This creates the public API for event registration of foo events. The generic notation <{bar: string}> indicates that the event object that the listeners receive has a field bar of the type string. This is in addition to target, type, and timeStamp that are available on every EventObject.

Note that we don’t need @component for @event to work. The event target does not even have to be a widget.

To trigger an event we can use the trigger method of Listeners available directly on onFoo.


This is the same as doing…

…except that the former is type-safe and the latter isn’t.

As you can see the new API also does not require us to create an EventObject instance, the new trigger method will do that for us and copy the given data to it.

Event Forwarding

Back to LabeledInput. In addition to change event we also want to be notified when the user presses enter while TextInput has the focus. The goal would be to enable the following code:

We will call the event accept, just like it does on TextInput. Lets start by adding the @event:

Now to forward the accept event from TextInput to LabeledInput we can simply do this:

But it can be even shorter:

This is possible because the trigger method of Listeners is permanently bound to event type and target, no matter how it is called. The event object that is passed on will not be re-used, instead trigger will copy the text property to a new EventObject instance with the correct type and target fields.

Putting it all together

Now LabeledInput looks like this:

And now we can update the demo snippet from last time to use the new accept event:

For now this completes our LabeledInput example component. For more example code using the Tabris decorators, have a look at the reddit_viewer project here.

Get Started with the Tabris Framework

To try out Tabris.js,

  • Install the new Tabris.js 2 developer app on your device
  • Try out the examples bundled in this app
  • Run your own code snippets from the playground, our online Tabris.js editor

Tabris.js 2 on Google Play Tabris.js 2 on Apple App StoreEnglish badge

To start developing real apps,

  • Install the latest Tabris CLI on your machine: npm install -g tabris-cli
  • Type tabris init in an empty directory – this will create a simple example app
  • Type tabris serve and load it in the developer app

The documentation contains everything you need to know (tip: try the doc search). Beginners find a step-by-step guide in this ebook. If you have questions or comments, you’re also invited to join the community chat.

Happy coding!