Bindable properties
How to create components that accept one or more bindable properties. You might know these as "props" if you are coming from other frameworks and libraries.
Bindable properties
When creating components, sometimes you will want the ability for data to be passed into them instead of their host elements. The
@bindable
decorator allows you to specify one or more bindable properties for a component.
The
@bindable
attribute also can be used with custom attributes as well as custom elements. The decorator denotes bindable properties on components on the view model of a component.
This will allow our component to be passed in values. Our specified bindable property here is called
loading
and can be used like this:
In the example above, we are binding the boolean literal
true
to the
loading
property.
Instead of literal, you can also bind another property (
loadingVal
in the following example) to the
loading
property.
As seen in the following example, you can also bind values without the
loading.bind
part.
Aurelia treats attribute values as strings. This means when working with primitives such as booleans or numbers, they won't come through in that way and need to be coerced into their primitive type using a bindable setter or specifying the bindable type explicitly using bindable coercion .
The
@bindable
decorator signals to Aurelia that a property is bindable in our custom element. Let's create a custom element where we define two bindable properties.
You can then use the component in this way,`
<name-component first-name="John" last-name="Smith"></name-component>
Calling a change function when bindable is modified
By default, Aurelia will call a change callback (if it exists) which takes the bindable property name followed by
Changed
added to the end. For example,
firstNameChanged(newVal, previousVal)
would fire every time the
firstName
bindable property is changed.
Due to the way the Aurelia binding system works, change callbacks will not be fired upon initial component initialization. If you worked with Aurelia 1, this behavior differs from what you might expect.
If you would like to call your change handler functions when the component is initially bound (like v1), you can achieve this the following way:
Configuring bindable properties
Like almost everything in Aurelia, you can configure how bindable properties work.
Change the binding mode using mode
You can specify the binding mode using the
mode
property and passing in a valid
BindingMode
to it;
@bindable({ mode: BindingMode.twoWay})
- this determines which way changes flow in your binding. By default, this will be
BindingMode.oneWay
Please consult the
binding modes
documentation below to learn how to change the binding modes. By default, the binding mode for bindable properties will be
one-way
Change the name of the change callback
You can change the name of the callback that is fired when a change is made
@bindable({ callback: 'propChanged' })
Bindable properties support many different binding modes determining the direction the data is bound in and how it is bound.
One way binding
By default, bindable properties will be one-way binding. This means values flow into your component but not back out of it (hence the name, one way).
Bindable properties without an
mode
explicitly set will be
one-way
by default. You can also explicitly specify the binding mode.
Two-way binding
Unlike the default, the two-way binding mode allows data to flow in both directions. If the value is changed with your component, it flows back out.
Working with two-way binding
Much like most facets of binding in Aurelia, two-way binding is intuitive. Instead of
.bind
you use
.two-way
if you need to be explicit, but in most instances, you will specify the type of binding relationship a bindable property is using with
@bindable
instead.
Explicit two-way binding looks like this:
The
myVal
variable will get a new value whenever the text input is updated. Similarly, if
myVal
were updated from within the view model, the input would get the updated value.
When using
.bind
for input/form control values such as text inputs, select dropdowns and other form elements. Aurelia will automatically create a two-way binding relationship. So, the above example using a text input can be rewritten to be
value.bind="myVal"
, and it would still be a two-way binding.
Bindable setter
In some cases, you want to make an impact on the value that is binding. For such a scenario, you can use the possibility of new
set
.
Suppose you have a
carousel
component in which you want to enable
navigator
feature for it.
In version two, you can easily implement such a capability with the
set
feature.
Define your property like this:
For
set
part, we need functionality to check the input. If the value is one of the following, we want to return
true
, otherwise, we return the
false
value.
-
''
: No input for a standalonenavigator
property. -
true
: When thenavigator
property set totrue
. -
"true"
: When thenavigator
property set to"true"
.
So our function will be like this
Now, we should set
truthyDetector
function as follows:
Although, there is another way to write the functionality too:
You can simply use any of the above four methods to enable/disable your feature. As you can see,
set
can be used to transform the values being bound into your bindable property and offer more predictable results when dealing with primitives like booleans and numbers.
Bindable & getter/setter
By default, you'll find yourself work with binable and field most of the time, like the examples given above. But there' cases where it makes sense to have bindable as a getter, or a pair of getter/setter to do more logic when get/set.
For example, a component card nav that allow parent component to query its active status. With bindable on field, it would be written like this:
Note that because
active
value needs to computed from other variables, we have to "actively" call
setActive
. It's not a big deal, but sometimes not desirable.
For cases like this, we can turn
active
into a getter, and decorate it with bindable, like the following:
Simpler, since the value of
active
is computed, and observed based on the properties/values accessed inside the getter.
Bindable coercion
The bindable setter section shows how to adapt the value is bound to a
@bindable
property. One common usage of the setter is to coerce the values that are bound from the view. Consider the following example.
Without any setter for the
@bindable
num we will end up with the string
'42'
as the value for
num
in
MyEl
. You can write a setter to coerce the value. However, it is a bit annoying to write setters for every
@bindable
.
Automatic type coercion
To address this issue, Aurelia 2 supports type coercion. To maintain backward compatibility, automatic type coercion is disabled by default and must be enabled explicitly.
There are two relevant configuration options.
enableCoercion
The default value is
false
; that is Aurelia 2 does not coerce the types of the
@bindable
by default. It can be set to
true
to enable the automatic type-coercion.
coerceNullish
The default value is
false
; that is Aurelia2 does not coerce the
null
and
undefined
values. It can be set to
true
to coerce the
null
and
undefined
values as well. This property can be thought of as the global counterpart of the
nullable
property in the bindable definition (see
Coercing nullable values
section).
Additionally, depending on whether you are using TypeScript or JavaScript for your app, there can be several ways to use automatic type coercion.
Specify
type
in
@bindable
type
in
@bindable
You need to specify the explicit
type
in the
@bindable
definition.
The rest of the document is based on TypeScript examples. However, we trust that you can transfer that knowledge to your JavaScript codebase if necessary.
Coercing primitive types
Currently, coercing four primitive types are supported out of the box. These are
number
,
string
,
boolean
, and
bigint
. The coercion functions for these types are respectively
Number(value)
,
String(value)
,
Boolean(value)
, and
BigInt(value)
.
Be mindful when dealing with
bigint
as the
BigInt(value)
will throw if the
value
cannot be converted to bigint; for example
null
,
undefined
, or non-numeric string literal.
Coercing to instances of classes
It is also possible to coerce values into instances of classes. There are two ways how that can be done.
Using a static
coerce
method
coerce
method
You can define a static method named
coerce
in the class used as a
@bindable
type. This method will be called by Aurelia2 automatically to coerce the bound value.
This is shown in the following example with the
Person
class.