In Aurelia 2 the @watch
decorator allows you to react effectively to data changes in your application, from simple properties to complex expressions. Think of it as computedFrom (if you’re coming from Aurelia 1) but on steroids.
Basics of @watch
The @watch
decorator in Aurelia 2 lets you define a function that will execute whenever a specified expression changes. This expression can be a simple property on your view model, a custom attribute, or a more complex expression involving the view-models properties.
Here’s a basic example of using @watch
to observe a property:
import { watch } from '@aurelia/runtime-html'; class MyApp { name = ''; @watch('name') onNameChange(newName, oldName) { console.log(`Name changed from ${oldName} to ${newName}`); } }
In this example, the onNameChange
method is triggered whenever the name
property changes.
Observing Single Values with String Expressions
For observing single properties, you can use simple string expressions. This approach is straightforward and valuable for simpler use cases.
class MyApp { packages = ['package1', 'package2']; @watch('packages.length') onPackagesChange(newLength, oldLength) { console.log(`Packages array length changed from ${oldLength} to ${newLength}`); } }
In this case, onPackagesChange
is triggered whenever an item is added to or removed from the packages
array.
Watching Complex Expressions with Function Expressions
Function expressions come into play when you need to observe more complex expressions. They allow you to construct intricate expressions involving property access, array methods, and more.
Here’s an example where we watch the full name of a runner
:
class MyApp { runner = { first: 'John', last: 'Doe' }; @watch((app: MyApp) => `${app.runner.first} ${app.runner.last}`) onRunnerChange(newFullName, oldFullName) { console.log(`Runner's full name changed from ${oldFullName} to ${newFullName}`); } }
In this example, onRunnerChange
is triggered whenever the first
or last
property of the runner
object changes.
Observing Array Methods: .map, .filter, and .find
The @watch
decorator is robust enough to observe transformations of arrays using methods like .map
, .filter
, and .find
.
Consider an array of numbers. We can watch the array transformed by the .map
method:
class MyApp { numbers = [1, 2, 3, 4, 5]; @watch((app: MyApp) => app.numbers.map(n => n * 2)) onDoubledNumbersChange(newNumbers, oldNumbers) { console.log(`Doubled numbers changed from ${oldNumbers} to ${newNumbers}`); } }
Here, onDoubledNumbersChange
is triggered whenever the numbers
array changes. The newNumbers
parameter represents the numbers
array mapped to its doubled values after the change. The oldNumbers
parameter represents the numbers
array mapped to its doubled values before the change.
Similarly, we can observe the result of the .filter
method:
class MyApp { numbers = [1, 2, 3, 4, 5]; @watch((app: MyApp) => app.numbers.filter(n => n % 2 === 0)) onEvenNumbersChange(newNumbers, oldNumbers) { console.log(`Even numbers changed from ${oldNumbers} to ${newNumbers}`); } }
Here, onEvenNumbersChange
is triggered whenever the array of even numbers changes.
The .find
method can also be used in an expression:
class MyApp { runners = [ { name: 'Alice', distance: 5 }, { name: 'Bob', distance: 10 }, { name: 'Charlie', distance: 15 }, ]; @watch((app: MyApp) => app.runners.find(r => r.distance > 10)) onFastestRunnerChange(newFastestRunner, oldFastestRunner) { console.log(`Fastest runner changed from ${oldFastestRunner.name} to ${newFastestRunner.name}`); } }
In this example, onFastestRunnerChange
is triggered whenever the runner who has run more than ten units changes.
Observing Nested Arrays
The @watch
decorator also supports watching nested arrays or objects. This enables you to observe changes in complex data structures.
class MyApp { teams = [ { name: 'Team 1', members: ['Alice', 'Bob'] }, { name: 'Team 2', members: ['Charlie', 'David'] }, ]; @watch((app: MyApp) => app.teams.map(t => t.members.length).reduce((a, b) => a + b, 0)) onTotalMembersChange(newTotal, oldTotal) { console.log(`Total members changed from ${oldTotal} to ${newTotal}`); } }
In this example, onTotalMembersChange
is triggered whenever a member is added to or removed from any team. The newTotal
and oldTotal
parameters represent the total number of team members after and before the change.
Conclusion
The @watch
decorator in Aurelia 2 is a powerful tool for maintaining reactivity in your applications. Its flexibility lets you observe changes in simple properties, complex expressions, and computed values. From watching single values with string expressions to observing complex expressions with array methods and function expressions, @watch
provides a simple and intuitive way to handle changes in your data.
Remember, Aurelia’s philosophy is convention over configuration, so it’s always a good idea to keep your code as simple and readable as possible. Utilize the @watch
decorator wisely to keep your UI up-to-date and your users happy.