Watching data
Watching data for changes, including support for expressions where you want to watch for changes to one or more dependencies and react accordingly.
Last updated
Watching data for changes, including support for expressions where you want to watch for changes to one or more dependencies and react accordingly.
Last updated
Unlike other observation strategies detailed in the observation section, the @watch
functionality allows you to watch for changes in your view models. If you worked with the computedFrom
decorator in Aurelia 1, the watch decorator is what you would replace it with.
Aurelia provides a way to author your components in a reactive programming model with the @watch
decorator. Decorating a class or a method inside it with the @watch
decorator, the corresponding method will be called whenever the watched value changes.
Intended usages
The @watch
decorator can only be used on custom element and attribute view models.
Corresponding watchers of @watch
decorator will be created once, bound after binding
, and unbound before unbinding
lifecycles. This means mutation during binding
/ after unbinding
won't be reacted to, as watchers haven't started or have stopped.
The @watch
decorator allows you to react to changes on a property or computed function.
In the following example, we will call a function every time the name property in our view model is changed.
This is referred to in Aurelia as an expression. You can also observe properties on things like arrays for changes which might be familiar to you if you worked with Aurelia 1 and the @computedFrom
decorator.
This is what an expression looks like observing an array length for changes:
Sometimes you want to watch multiple values in a component, so you need to create an expression. A computed function is a function provided to the @watch
decorator, which allows you to do comparisons on multiple values.
To illustrate how you can do this, here is an example:
In this example, the log
method of PostOffice
will be called whenever a new package is added to, or an existing package is removed from the packages
array. The first argument of our callback function is the view model, allowing us to access class properties and methods.
The method name will be used to resolve the function once, which means changing the method after the instance has been created will not be recognized.
During
binding
lifecycle, bindings created by@watch
decorator haven't been activated yet, which means mutations won't be reacted to:
There will be no log in the console.
During
bound
lifecycle, bindings created by@watch
decorator have been activated, and mutations will be reacted to:
There will be 1 log in the console that looks like this: packages changes: 0 -> 1
.
Other lifecycles that are invoked after binding
and before unbinding
are not sensitive to the working of the @watch
decorator, and thus don't need special mentions. Those lifecycles are attaching
, attached
, and detaching
.
During
detaching
lifecycle, bindings created by@watch
decorator have not been de-activated yet, and mutations will still be reacted to:
There will be 1 log in the console that looks like this: packages changes: 0 -> 1
.
During
unbinding
lifecycle, bindings created by@watch
decorator have been deactivated, and mutations won't be reacted to:
There will be no log in the console.
By default, a watcher will be created for a @watch()
decorator. This watcher will start observing before bound
lifecycle of components. How the observation works will depend on the first parameter given.
If a string or a symbol is given, it will be used as an expression to observe, similar to how an expression in Aurelia templating works.
If a function is given, it will be used as a computed getter to observe dependencies and evaluate the value to pass into the specified method. Two mechanisms can be employed:
For JavaScript environments with native proxy support: Proxy will be used to trap & observe property read. It will also observe collections (such as an array, map and set) based on invoked methods. For example, calling .map(item => item.value)
on an array should observe the mutation of that array and the property value
of each item inside the array.
For environments without native proxy support: the 2nd parameter inside the computed getter can be used to observe (or register) dependencies manually. This is the corresponding watcher created from a @watch
decorator. It has the following interface:
An example is:
The firstName
and lastName
properties of contact
components are being observed manually. And every time either firstName
, or lastName
change, the computed getter is run again, and the dependencies will be observed again. Observers are cached, and the same observer won't be added more than once, old observers from the old computed getter run will also be disposed of, so you won't have to worry about stale dependencies or memory leaks.
Automatic array observation
By default, in the computed getter, array mutation method such as .push()
, .pop()
, .shift()
, .unshift()
, .splice()
, and .reverse()
are not observed, as there are no clear indicators of the dependencies to collect from those methods.
It is best to avoid mutation on dependencies collected inside a computed getter.
To ensure identity equality with proxies, always be careful with objects not accessed from the first parameter passed into the computed getter. Better get the raw underlying object before doing the strict comparison with ===
.
In this example, even if options
on a MyClass
instance has never been changed, the comparison of myClass.options === defaultOptions
will still return false, as the actual value for myClass.options
is a proxied object wrapping the real object, and thus is always different with defaultOptions
.
Dependency tracking inside a watch computed getter is done synchronously, which means returning a promise or having an async function won't work properly.
Don't do the following:
Name | Type | Description |
---|
expressionOrPropertyAccessFn | string | IPropertyAccessFn | Watch expression specifier |
changeHandlerOrCallback | string | IWatcherCallback | The callback that will be invoked when the value evaluated from watch expression has changed. If a name is given, it will be used to resolve the callback |