I would like to observe all changes on an object properties.
In the following example, i would like to be notified by the personChanged observer if firstname or lastname is changed.
BUT I would like to have something generic applied on all object properties (using Ember.keys() ??)
How to replace 'firstname', 'lastname' with something more generic ?
In my example:
personChanged
is called when firstname
or lastname
is changed:
App.MyObject: Ember.Object.create({
firstname: 'first',
lastname: 'last',
personChanged: Ember.observer(function() {
console.log("person changed");
}, 'firstname', 'lastname')
})
This is possible using an ObjectProxy
in two flavours, depending on your requirements. Both approaches differ only in when and how many times is the observer called and both of them rely on Ember.keys
.
The HTML for both the solutions is the same.
HTML
<script type="text/x-handlebars" data-template-name="app">
Name: {{App.MyObject.firstname}} {{App.MyObject.lastname}}
<ul>
{{#each App.List}}
<li>{{this}}</li>
{{/each}}
</ul>
</script>
Solution 1
JsFiddle: http://jsfiddle.net/2zxSq/
Javascript
App = Em.Application.create();
App.List = [];
App.MyObject = Em.ObjectProxy.create({
// Your Original object, must be defined before 'init' is called, however.
content: Em.Object.create({
firstname: 'first',
lastname: 'last'
}),
// These following two functions can be abstracted out to a Mixin
init: function () {
var self = this;
Em.keys(this.get('content')).forEach(function (k) {
Em.addObserver(self.get('content'), k, self, 'personChanged')
});
},
// Manually removing the observers is necessary.
willDestroy: function () {
var self = this;
Em.keys(this.get('content')).forEach(function (k) {
Em.removeObserver(self.get('content'), k, self, 'personChanged');
});
},
// The counter is for illustrative purpose only
counter: 0,
// This is the function which is called.
personChanged: function () {
// This function MUST be idempotent.
this.incrementProperty('counter');
App.List.pushObject(this.get('counter'));
console.log('person changed');
}
});
App.ApplicationView = Em.View.extend({
templateName: 'app'
});
// Test driving the implementation.
App.MyObject.set('firstname', 'second');
App.MyObject.set('lastname', 'last-but-one');
App.MyObject.setProperties({
'firstname': 'third',
'lastname' : 'last-but-two'
});
While initialising MyObject
, all properties which already exist on the content
object are observed, and the function personChanged
is called each time any of the property changes. However, since the observers are fired eagerly [1], the function personChanged
should be idempotent, which the function in example is not. The next solution fixes this by making the observer lazy.
Solution 2
JsFiddle: http://jsfiddle.net/2zxSq/1/
Javascript
App.MyObject = Em.ObjectProxy.create({
content: Em.Object.create({
firstname: 'first',
lastname: 'last'
}),
init: function () {
var self = this;
Em.keys(this.get('content')).forEach(function (k) {
Em.addObserver(self, k, self, 'personChanged')
});
},
willDestroy: function () {
var self = this;
Em.keys(this.get('content')).forEach(function (k) {
Em.removeObserver(self, k, self, 'personChanged');
});
},
// Changes from here
counter: 0,
_personChanged: function () {
this.incrementProperty('counter');
App.List.pushObject(this.get('counter'));
console.log('person changed');
},
// The Actual function is called via Em.run.once
personChanged: function () {
Em.run.once(this, '_personChanged');
}
});
The only change here is that the actual observer function is now called only at the end of the Ember Run loop, which might be the behaviour you are looking for.
Other notes
These solutions use ObjectProxy
instead of defining the observers on the object itself to avoid setting spurious observers (on properties such as init
, willDestroy
, etc.) or an explicit list of properties to observe.
This solution can be extended to start observing dynamic properties by overriding the setUnknownProperty
on the proxy to add an observer every time a key is added to content
. The willDestroy
will remain the same.
Reference
[1] This might get changed soon thanks to Asyn Observers
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With