@property

:point_right: Make sure to first read the introduction to data binding.

Makes the decorated object property fire change events and perform runtime value checks, which is the expected behavior of widget properties in Tabris.js. It makes the property a valid source for one-way data bindings on @component decorated widgets. The @property decorators can be used in any class, not just subclasses of Widget. On a non-widget class change events may be listened to via an instance of ChangeListeners attached to an appropriately named property.

@property (no parameter)

See example apps “property-change-events” (TypeScript) and “property-change-events-jsx” (JavaScript/JSX).

Triggers change events and (in TypeScript) performs implicit runtime checks on any value the property is set to.

class Foo {
  @property myText: string = 'foo';
  @event onMyTextChanged: ChangeListeners<Foo, 'myText'>;
}

const foo = new Foo();
foo.onMyTextChanged(ev => console.log(ev.value));
foo.myText = 'bar'; // logs "bar" due to the change event
(foo as any).myText = 23; // throws due to the implicit value check

In JavaScript these checks do not happen unless the type option is set.

The implicit runtime check only works with primitive types and classes. Advanced type and interfaces can not be checked:

class Foo {
  @property myItem: {bar: string};
}

const foo = new Foo();
foo.myItem = {bar: 'foo'}; // OK
(foo as any).myItem = {foo: 'bar'}; // runtime check passes despite incorrect type

In these cases it is recommended to use a type guard.

@property(config)

Like @property, but with more options.

config.typeGuard

Where typeGuard is of the type (value: any) => boolean.

See example app “property-value-checks”.

Uses the given function (type guard) to perform type checks. The type guard may be more strict than the TypeScript compiler (e.g. allowing only positive numbers where the compiler allows any number), but should never be less strict.

The function may return either a boolean (true indicates the value passes), or throw an error explaining why the value did not pass.

Example for a type guard more strict than the compiler:

  @component
  class CustomComponent extends Composite {

    @property(v => v instanceof Array || (!isNaN(v) && v >= 0))
    mixedType: number[] | number = 0; // compiler would allow -1, but not the type guard

  }

By using multiple @property decorator on the same property you can give multiple type guards. They are executed in the given order.

config.type

This option is only useful in JavaScript/JSX files.

Where type is a constructor function.

When providing this option the property will check that every assigned value (except null) is an instance of the given constructor function, such as Date. Primitives (number, string, boolean) are represented by the constructors of their boxed/wrapped values.

JavaScript Example:

class Foo {

  /** @type {Date} */
  @property({type: Date})
  myDate = new Date();

  /** @type {string} */
  @property({type: String})
  myText = 'foo';

}

This is the exact equivalent of the following TypeScript code:

class Foo {
  @property myDate: Date = new Date();
  @property myText: string = 'foo';
}