So you’ve run into a wall or perhaps just curious on how to expand your observer-fu? Today we are going to be learning how to observe Arrays and Objects in Aurelia.
Many of the concepts you will learn here can be used to use Aurelia components outside of Aurelia, once you understand the rules you can use them to your advantage.
Why you won’t see @observable
here
In another post here I talk about the @observable
decorator and how you can use it, but you won’t see it being used here.
The @observable
decorator and underlying functionality is only for observing class properties, it does not work for arrays or objects (unless you reassign them). When working with arrays, you’ll be; splicing, pushing and popping so it won’t work for those cases.
Observing Arrays
Using the Aurelia Collection Observer we can observe array changes and our subscribe method will return splices.
The syntax kind of looks similar to that of the Event Aggregator, except we’re observing a collection and not working with events.
import {BindingEngine, inject} from 'aurelia-framework';
@inject(BindingEngine)
export class MyClass {
constructor(bindingEngine) {
this.bindingEngine = bindingEngine;
this.theList = [];
let subscription = this.bindingEngine.collectionObserver(this.theList).subscribe(this.listChanged);
// Dispose of observer when you are done via: subscription.dispose();
}
listChanged(splices) {
}
}
What are splices?
You probably assume that “splices” is simply a new array of added items on our callback. This is not the case. The splices argument returns an object with three properties; addedCount
, index
and an array called removed
.
Using these values, we can determine if an item was inserted or removed in an array. In the case of removed
, when we remove an item from an array it will be in this removed
array so we have a record of the deletion.
Observing Object Properties
In Aurelia you can use the BindingEngine class which has a propertyObserver
method which handles observing object properties.
Once again, the syntax looks identical to our array example above, except the propertyObserver
method requires the object we are observing as the first argument and the second argument is a string name of the property.
import {BindingEngine, inject} from 'aurelia-framework';
@inject(BindingEngine)
class MyClass {
constructor(bindingEngine) {
this.bindingEngine = bindingEngine;
this.observeMe = 'myvalue';
let subscription = this.bindingEngine
.propertyObserver(this, 'observeMe')
.subscribe((newValue, oldValue) => { this.objectValueChanged(newValue, oldValue) });
// Dispose of observer when you are done via: subscription.dispose();
}
objectValueChanged(newValue, oldValue) {
console.log(`observeMe value changed from: ${oldValue} to:${newValue}`);
}
}
Unlike our collection observer, we don’t get splices back and get the values themselves. The propertyObserver
works very similar to how @bindable
attributes in custom attributes and elements does with a changed callback.
Conclusion
The collectionObserver
is for collections and not only does it work for arrays, but you can use it on other collection types like a Map
as well. The propertyObserver
is for object properties themselves. That’s all you need to remember.
What are splices exactly?
@Sagar Thakur
Splices returns an object which has some properties detailing the mutation the took place. The number of items added, index and an array of removed items.
What if I would like to access properties of the class like this.xy=”test” in objectValueChanged(newValue, oldValue)? It seems that “this” is undefined in objectValueChanged.
@Holger
You have propertyObserver for that.
I thought with the 1.0 beta (or perhaps it was the previous releases) there was a change toward using the bindingEngine. This works great for object binding/listening and is much cleaner code wise. I have not tried it on array size changes to know whether or not it will work the same here. My understanding is it takes care of the ObserverLocator stuff under the covers.
@Holger,
> What if I would like to access properties of the class like this.xy=”test” in objectValueChanged(newValue, oldValue)? It seems that “this” is undefined in objectValueChanged.
The problem is that the context is lost when just passing function reference to subscribe method as a callback.
@Dwayne
Could You update the example from
> .subscribe(this.objectValueChanged);
to
> .subscribe((newValue, oldValue) => { this.objectValueChanged(newValue, oldValue)});
That would fix the problem Holger had.
I want to call same subscribe event on 4 observable values.
I want to call same subscribe event on 4 observable values.
Is there any short code for this type of scenario.
this.observeMe = ‘myvalue’;
this.observeMe2 = ‘myvalue2’;
this.observeMe3 = ‘myvalue3’;
let subscription = this.bindingEngine
.propertyObserver(this, ‘observeMe observeMe2 observeMe3’)
.subscribe(this.objectValueChanged);
How do you test this?
Hi Dwayne, thank you for your excellent posts!
How do these Observables relate to Rxjs? For example, if I want to “import” a bacon.js observable into Rxjs I can do so.
What I’m trying to do is throttle, debounce, filter or map a changed property. I want to put a delay into the event stream so as to no query the server too fast.
Is this possible in Auralia? If not, can I use RxJS and pass in the observable returned from the propertyObserver? As last resort, is there some other way I’ve missed?
Thank you in advance,
Carlos
how do i observe an entire object not one property