App.Person = Ember.Object.extend({
firstName: "Tom",
surname: "Close",
name: function() {
return this.get('firstName') + " " + this.get('surname');
}.property('firstName', 'surname'),
});
var tom = App.Person.create();
tom.firstName
// "Tom"
tom.firstName = "Thomas"
// "Thomas"
tom.get('firstName')
// "Thomas"
tom.firstName = "Alan"
// "Alan"
tom.get('name')
// "Alan Close"
tom.firstName = "Tom"
// Error: Assertion Failed: You must use Ember.set() to access this property (of <App.Person:ember381>)
If we look at the object we see that we now have a few new functions:
tom
Class {constructor: function, _super: function, firstName: "Tom", surname: "Close", title: "Mr"…}
// snip
get firstName: function () {
set firstName: function (value) {
get surname: function () {
set surname: function (value) {
Ember has used defineProperty
to provide setters and getters for when you call
tom.firstName' and
tom.firstName=. The setter will just raise the
error that we hit above; the getter looks in the
meta[cache]` for the
stored.
The relevant file here is ember-metal/libs/property-get.js
. I’m a
little unsure why this isn’t triggered when you just do
tom.get('firstName')
though.
In ember-metal/libs/property-set.js
we see that the setter calls
propertyDidChange(obj, keyname)
on setting (if the property is a
vanilla non-descriptor sort). This notifies dependent keys, chains and
observers (if anything is watching that key). We can call these
manually.
For the dependent keys, it iterates over the graph (stored in
meta[deps]), calling propertyDidChange on each key. On vanilla keys this
does nothing, but on descriptors it calls .didChange(obj, keyname)
.
Calling didChange(obj, keyname)
on a ComputedProperty sets the cached
vaule to undefined
and removes the dependentKeys.
~~~js
ComputedPropertyPrototype.didChange = function(obj, keyName) {
// suspended is set via a CP.set to ensure we don’t clear
// the cached value set by the setter
if (this.cacheable && this._suspended !== obj) {
var meta = metaFor(obj);
if (meta.cache[keyName] !== undefined) {
meta.cache[keyName] = undefined;
removeDependentKeys(this, obj, keyName, meta);
}
}
};
~~~
RemoveDependentKeys 1. Looks up on the descriptor to see what its dependent keys are (‘firstName’, 'surname’) 2. Reduces the count of the property ('name’) in the dependent keys. 3. Stops watching.
printMeta(tom)
== Cache ==
name: Tom Close
== Values ==
firstName: Tom
surname: Close
== Watching ==
firstName: 1
surname: 1
== Deps ==
firstName:
name: 1
surname:
name: 1
== Chain watchers ==
== Chains ==
> Em.propertyDidChange(tom, 'name')
undefined
> printMeta(tom)
== Cache ==
== Values ==
firstName: Tom
surname: Close
== Watching ==
firstName: 0
surname: 0
== Deps ==
firstName:
name: 0
surname:
name: 0
== Chain watchers ==
== Chains ==
So that’s ok for properties. What about chains?
Aside: defining properties: ~~~js // define a simple property Ember.defineProperty(contact, 'lastName’, undefined, 'Jolley’);
// define a computed property Ember.defineProperty(contact, 'fullName’, Ember.computed(function() { return this.firstName+’ ’+this.lastName; }).property('firstName’, 'lastName’)); ~~~
tom.get('locationName')
null
printMeta(tom)
== Cache ==
locationName: null
== Values ==
location: null
== Watching ==
location.name:
location: 1
== Deps ==
location.name:
locationName: 1
== Chain watchers ==
location:
0:
{Key: location, Value: null, Object: <App.Person:ember252>, Watching: true, Count: 1}
-> {Key: null, Value: <App.Person:ember252>, Object: undefined, Watching: false, Count: 0}
== Chains ==
root: {Key: null, Value: <App.Person:ember252>, Object: undefined, Watching: false, Count: 0}
location: {Key: location, Value: null, Object: <App.Person:ember252>, Watching: true, Count: 1}
name: {Key: name, Value: undefined, Object: null, Watching: true, Count: 1}
undefined
ChainNode.chainsWillChange ChainNode.willChange
seems to just walk the chain lattice, collecting the object/property
pairs to fire the propertyWillChange events on. Which in turn
essentially just fires didChange
on the descriptors. Which just clears
the cache and removes the dependent keys