Watching data
Watch data changes reactively with the @watch decorator. Support for properties, expressions, and computed values with automatic dependency tracking.
The @watch decorator enables reactive programming in Aurelia by automatically responding to changes in your view model properties or computed expressions. When data changes, your callback is invoked with the new and old values.
import { watch } from '@aurelia/runtime-html';
class UserProfile {
firstName = 'John';
lastName = 'Doe';
@watch('firstName')
nameChanged(newName, oldName) {
console.log(`Name changed from ${oldName} to ${newName}`);
}
}Watchers activate after the binding lifecycle and deactivate before unbinding, so changes during component initialization or cleanup won't trigger callbacks.
Two Ways to Use @watch
expressionOrPropertyAccessFn
string or IPropertyAccessFn
Specifies the value to watch. When a string is provided, it is used as an expression (similar to Aurelia templating). When a function is provided, it acts as a computed getter that returns the value to observe.
changeHandlerOrCallback
string or IWatcherCallback
Optional. The callback invoked when the watched value changes. If a string is provided, it is used to resolve a method name (resolved only once, so subsequent changes to the method are not tracked). If a function is provided, it is called with three parameters: new value, old value, and the instance.
options
IWatchOptions
Optional. Configuration options for the watcher, including flush timing control.
Watch Options
The options parameter accepts an IWatchOptions object with the following properties:
flush
'async' | 'sync'
'async'
Controls when the watcher callback is executed. 'async' (default) defers execution to the next microtask, while 'sync' executes immediately.
Method Decorator (most common):
Class Decorator (with separate callback):
What You Can Watch
Simple Properties
Nested Properties
Array Properties
Symbol Properties
Numeric Property Keys
Computed Watchers
When you need to watch multiple properties or complex expressions, use a computed function that returns the value to observe:
The computed function receives your view model as its first parameter. Aurelia automatically tracks which properties your function accesses and will re-run it when any of those properties change.
Complex Computed Example
This watcher automatically re-runs when:
Items are added/removed from
tasksThe
doneproperty changes on any taskThe
filterproperty changes
Real-World Examples
API Data Synchronization
Form Validation
State Management
Dynamic UI Updates
Watcher Lifecycle
Watchers follow component lifecycle and only respond to changes when the component is properly bound:
binding
❌ No
Setup code won't trigger watchers
bound → detaching
✅ Yes
All changes trigger callbacks
unbinding
❌ No
Cleanup code won't trigger watchers
This lifecycle integration prevents watchers from firing during component initialization and cleanup, avoiding unwanted side effects.
Flush Modes
Control when watcher callbacks execute with flush modes:
Async (Default): Batches multiple changes and executes callbacks asynchronously. Prevents infinite loops and improves performance.
Sync: Executes callbacks immediately. Use only when you need instant feedback or in testing scenarios.
Manual Watcher Classes
The decorator syntax is sugar over two exported classes: ComputedWatcher (getter-based) and ExpressionWatcher (template expression–based). Creating them directly is handy when you are writing tooling, want to observe arbitrary objects, or need to opt in/out of watching at runtime.
ComputedWatcher
ComputedWatcherPass the object you want to observe as the first argument.
The getter runs inside Aurelia's dependency tracker; any property you touch becomes a dependency.
Always call
unbind()(for example, during theunbindinglifecycle) so observers are released.
ExpressionWatcher
ExpressionWatcherExpressionWatcher observes a parsed Aurelia expression, making it ideal for runtime-configurable dashboards or devtools.
Because watchers are plain classes, you can create them inside services or diagnostics tooling as well. The only requirements are an IObserverLocator, an IServiceLocator (usually IContainer), and manual lifecycle management.
How Dependency Tracking Works
Aurelia uses transparent proxy-based observation. When your computed function runs, it automatically tracks every property you access:
The watcher re-runs whenever:
The
playersarray changes (items added/removed)Any player's
isActiveproperty changesYou replace the entire
playersarray
Manual Dependency Registration
In environments without proxy support, you receive a second parameter to manually register dependencies:
The watcher parameter provides:
observe(obj, key)- Watch a propertyobserveCollection(collection)- Watch arrays, Maps, or Sets
Callback Signature Details
All watch callbacks receive three parameters:
Class Decorator with Callback Function
Method Name as String Callback
Important: When using method name strings, the method is resolved only once when the class is defined. Dynamically changing the method later won't affect the watcher.
Best Practices
✅ Do's
Keep computed functions pure:
Use descriptive callback names:
Prefer method decorators over class decorators:
❌ Don'ts
Don't mutate data in computed functions:
Don't use async functions:
Don't create infinite loops:
Performance Tips
Use
{ flush: 'async' }(default) for better performanceAvoid deeply nested property access in hot paths
Consider debouncing expensive operations:
Common Errors and Troubleshooting
1. Choose the Right Flush Mode
2. Avoid Mutating Dependencies in Computed Getters
3. Understand Collection Observation
4. Avoid Mutating Dependencies in Computed Getters
Do not alter properties or collections when returning a computed value:
5. Be Cautious with Object Identity
Error: AUR0774 - Static Method Decoration
// ✅ Fix: Use instance methods only class App { @watch('counter') handleChange() { /* ... */ } }
Computed Function Errors
When computed functions throw errors, callbacks won't execute:
6. Do Not Return Promises or Async Functions
Circular Dependencies
Avoid modifying watched properties in their own callbacks:
Advanced Watch Patterns
Deep Object Observation
The @watch decorator can observe deeply nested properties and complex expressions:
Collection Observation Patterns
Observable Array Methods
Different array methods have varying levels of observability:
Map and Set Observation
Flush Timing Control
The flush option controls when watcher callbacks are executed:
Performance Considerations
When to Use Different Patterns
Optimizing Watch Expressions
Last updated
Was this helpful?