Tabris Decorators Part 3: Data Binding Basics

In this article, we will cover how TypeScript decorators are used in the Tabris Framework to enable data binding for UI Components. It is a part of the blog post series dedicated to the Tabris.js extension called tabris-decorators, which makes developing mobile apps with Tabris.js 2.x and TypeScript more convenient. The extension features data binding capabilities, enhanced event handling, and dependency injection, explicitly designed for the Tabris mobile framework.

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

  • Tabris Decorators Part 1: Intro to TypeScript Decorators
  • Tabris Decorators Part 2: Introducing Tabris Components

  • In this chapter, we take a first look at data binding. The @component decorator allows for simple one-way and two-way bindings between a property of a custom component instance and a property of one of its children. This also involves the @propertyand @bind decorators and new JSX syntax, all provided by the tabris-decorators module

    The story so far…

    We will continue from where we left off in the previous chapter with the LabeledInput example. The current state is that we have a simple component containing a TextView and a TextInput. Except for the constructer it does not provide any API of its own:

    It can be used like this:

    However, right now the text property of the TextView is hard-coded to the 'Label:'. Next we will add a labelText property to the LabeledInput that will allow setting its value as a JSX attribute:

    If the labelText property was final (cannot change after widget construction) it could be set via constructor parameter.

    Simply add a new property to the class:

    That makes the labelText part of the properties parameter of the LabeledInput constructor. Now we can set its value directly on the child element:

    But that would mean that any changes to the labelText after a widget construction, would have no effect on the actual textView element. Data-binding solves this problem.

    One-way bindings

    In essence, we want to be able to do this:

    What we need here, is a one-way data binding which applies changes of the labelText property (the “base property”) to the text property on the “label” TextView (the “target property”). Since the TextView is not interactive, there is no need to have a “reverse” data flow that reflects changes on the target property back to the base.

    One-way bindings first require a base property decorated with a @property, like this:

    This makes the property eligible for one-way bindings. We could also initialize the property with the declaration, but in this case, we don’t really have a useful default value for it.

    Next we establish the actual binding. The @component decorator enables a new JSX attribute syntax where an existing attribute of a child is prefixed with a bind-. The value of that attribute is the name of a base property to which it shall be bound. So in our case this becomes:

    And voilà: All changes to the labeledInput.text will from now on be applied to the “label” TextView.

    This is not all the @property decorator can do. It will pop up again in the next chapter on an event handling and in the second chapter on data binding.

    Two-way bindings

    Next we will add a property representing the actual text entered by the user. We will call it a text :

    It differs from the labelText in that the value can change by itself due to user interactions. If we just used a one-way binding as above, this would not work as expected:

    The text property would always provide the same value, even if the user has edited the text in the TextInput widget.

    So here is where we need a two-way binding that keeps both base and target properties in sync with each other. This is accomplished by attaching the @bind decorator to the declaration of the base property:

    The string given to the decorator takes a binding path which follows the pattern '#<targetWidgetId>.<targetPropertyName>'.

    As a result we end up with a class like this:

    Now, to prove that everything actually works, let’s write a mini form for entering your name. The result of the user input will be shown in a dialog.



    Some additional remarks for Data Binding Basics:

    • Components are initialized when append is called the first time. All bindings of any kind (i.e. one- and two-way bindings) are established at that moment, and appending any more widgets later, has no effect. This may change in Tabris.js 3.
    • The IDE / TypeScript compiler can not check that the bindings will be valid, so no auto completion is available for this syntax. However, at runtime any invalid bindings will throw errors, they will not just fail silently.
    • Binding to non-components (i.e. models or other widgets) is not fully implemented yet.
    • We will discuss edge-cases and limitations in another blog post further down the road.

    That’s it with the basics of the tabris-decorators data binding features . The next chapter will deal with an event handling, continuing with the same example class.

    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!