Even if you are new to Aurelia, you are probably familiar with the @bindable
functionality which allows you to add bindable attributes primarily to your custom elements and custom attributes.
There is a lesser known feature in Aurelia in the form of @observable
which allows you to use the underlying observation layer in Aurelia to observe variables for changes and react accordingly in a similar way you react to changes on @bindable
attributes.
@observable
In Action
import { observable } from 'aurelia-framework';
export class MyViewModel {
@observable myVariable;
attached() {
setTimeout(() => {
this.myVariable = 'I am a value, hooray!';
}, 5000);
}
myVariableChanged(newValue, oldValue) {
console.log(newValue, oldValue);
}
}
Once the view-model attaches itself to the page, it’ll wait 5 seconds and then change the value. This will then trigger the changed callback defined on the view-model.
The naming convention is <variableName>Changed
with two parameters on the changed callback, the new value and the old value.
The variableChanged part is a convention. Aurelia takes for supplied class value and appends Changed
to the end. If you do not like this kind of magic, you can explicitly configure the observable
decorator to define the callback.
import { observable } from 'aurelia-framework';
export class MyViewModel {
@observable({changeHandler: 'myVariableChanged'}) myVariable;
attached() {
setTimeout(() => {
this.myVariable = 'I am a value, hooray!';
}, 5000);
}
myVariableChanged(newValue, oldValue) {
console.log(newValue, oldValue);
}
}
This does the exact same thing as the previous example, we are just being more explicit. This allows you to also change the change callback function. If you wanted your change handler to be called ‘myExplicitChangeHandler` then you’d do this:
@observable({changeHandler: 'myExplicitChangeHandler'}) myVariable;
myExplicitChangeHandler(newValue, oldValue) {
console.log(newValue, oldValue);
}
The beautiful thing about the @observable
decorator is behind the scenes all it does is create a setter and getter on your property but also declares any dependencies your property has which is the equivalent of @computedFrom
which prevents dirty-checking.
There are other ways you can observe changes on custom objects and arrays, one such alternative is using the BindingEngine
and observation methods, specifically propertyObserver
if you want to observe changes on objects.
Does it detect `myVariableChanged` by convention? In that case I think that I still prefer explicit Knockout Observables…
@Andres
Behind the scenes observable just defines standard setter/getters for your properties and registers a change event listener. You can define what the callback function is just like you can using a `@bindable`
I understand, but in my opinion this kind of ~~magic~~ conventions is not good.
Andres, it’s really not “magic”. If it has the attribute @bindable, or @observable, it gets called, otherwise, it does not get called.
Adding the attribute is just a shortcut, not magic at all.
BTW, for anyone reading…
Unless I just have syntax wrong, it appears that @bindable and @observable will NOT invoke a lambda expression, i.e.,
nameChanged =(old,newval)=>{
//does not get called
}
where should i import observable from?
observable is in ‘aurelia-framework’
Also nice stuff in ‘aurelia-framework’
bindable, computedFrom, inject
I noticed there is no dispose/unsubscribe in your examples. Is the event listener disposed of automatically when the component deactivates?
i was insiting using @bindable, thanks you saved my day, again 🙂