When it comes to objects, Aurelia couldn’t make it any easier to observe them for changes and react accordingly and in this topic, we’ll be showing you how easy it is to watch object properties for changes in your Aurelia applications.
Say hello to BindingEngine
Aurelia’s binding system provides an interface called BindingEngine
which exposes a few methods for watching not only objects, but collections like arrays and maps. We’ll talk about other collections in a separate topic.
The BindingEngine
has a method called propertyObserver
which accepts two arguments. The first argument is the object you want to observe and the second argument is the name of the property (as a string).
If you are familiar with the Event Aggregator, then the syntax for observing object properties will look very similar in the form of subscribing to the property change via the subscribe
method.
The subscribe
method works very similarly to how @observable
and @bindable
works (if you’re familiar with those). When a value changes, a change callback is fired with the new value as the first argument and old (previous) value as the second. If there is no previous value, it’ll be undefined.
A basic example
import {BindingEngine} from 'aurelia-framework';
class MyViewModel {
static inject = [BindingEngine];
constructor(bindingEngine) {
this.bindingEngine = bindingEngine;
this.observeMe = 'myvalue';
}
attached() {
this.subscription = this.bindingEngine
.propertyObserver(this, 'observeMe')
.subscribe((newValue, oldValue) => {
this.objectValueChanged(newValue, oldValue)
});
}
detached() {
this.subscription.dispose();
}
objectValueChanged(newValue, oldValue) {
console.log(`observeMe value changed from: ${oldValue} to:${newValue}`);
}
}
In our above example we are injecting the BindingEngine
instance into our view-model, storing it on the class so we can use it and then setting up a subscription to an object property.
The best place to setup any kind of observer or event is within attached
so we can store the reference and then inside of detached
when the view-model gets teared down, we dispose of it to free up the memory and let the natural browser garbage collection do its thang.
You might notice we are not observing an object, we are actually observing the current view-model and a specific property on it. We won’t get into specifics on classes, but all you need to know is functions/classes with properties work the same way as a plain old object.
In our subscribe
handler, we are using an arrow function (to retain scope to the current view-model) and then manually firing a change callback ourselves. If you were to pass the callback directly, you’d lose the current scope.
In this example, our change callback will only fire once (when the value is initialized). We won’t have a previous value either, so the second argument on our change callback will be undefined. So, now we’ll make it so the value changes a couple of times and see what values our change callback is given.
Triggered changes
Taking the code we wrote earlier, let’s add in a setInterval
which changes a property value every 2 seconds. This means you should see the change callback firing once every two seconds.
import {BindingEngine} from 'aurelia-framework';
class MyViewModel {
static inject = [BindingEngine];
constructor(bindingEngine) {
this.bindingEngine = bindingEngine;
this.observeMe = 'myvalue';
}
attached() {
this.subscription = this.bindingEngine
.propertyObserver(this, 'observeMe')
.subscribe((newValue, oldValue) => {
this.objectValueChanged(newValue, oldValue)
});
setInterval(() => {
this.observeMe = 'Cool date ' + new Date();
}, 2000);
}
detached() {
this.subscription.dispose();
}
objectValueChanged(newValue, oldValue) {
console.log(`observeMe value changed from: ${oldValue} to:${newValue}`);
}
}
If you open up your browser developer tool console, you should see a change console.log
with the previous and current value being displayed. Because we used setTimeout
you’ll see it continually spam the console.
Caveats
The propertyObserver
doesn’t really have many caveats, the only thing you need to be aware of is you can only watch a single property. You can create multiple propertyObserver
calls to observe multiple properties, but you cannot observe objects as a whole (as there are no native events fired when they change).
Conclusion
The propertyObserver
method is simple and powerful. Use it to watch object properties on classes and even injected singletons from elsewhere in your application to reactively perform actions based on property changes.