Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 19 additions & 5 deletions packages/ember-metal/lib/chains.js
Original file line number Diff line number Diff line change
Expand Up @@ -324,12 +324,26 @@ ChainNodePrototype.didChange = function(events) {

export function finishChains(obj) {
// We only create meta if we really have to
var m = obj['__ember_meta__'], chains = m && m.chains;
if (chains) {
if (chains.value() !== obj) {
var m = obj['__ember_meta__'],
chains, chainWatchers, chainNodes;
if (m) {
// finish any current chains node watchers that reference obj
chainWatchers = m.chainWatchers;
if (chainWatchers) {
for(var key in chainWatchers) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Object.keys or custom chainsNodesFrom instead of hasOwnProperty check should prevent potential deopt of key in chainWatchers

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Slight pedantic note: if we need Object.keys behavior we should use Ember.keys instead (it is basically Object.keys when that exists but still works in ES3 browsers).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I know, this isn't the slow case (chainWatchers are always simple objects and copied), and I'm hesitant to make this change as part of this pull, since none of the other code in chains.js is using keys over for/in, I'd rather see the switch to keys() uniformly in chains with some cross browser perf data.

if (!chainWatchers.hasOwnProperty(key)) { continue; }
chainNodes = chainWatchers[key];
if (chainNodes) {
for (var i=0,l=chainNodes.length;i<l;i++) {
chainNodes[i].didChange(null);
}
}
}
}
// copy chains from prototype
chains = m.chains;
if (chains && chains.value() !== obj) {
metaFor(obj).chains = chains = chains.copy(obj);
} else {
chains.didChange(null);
}
}
}
Expand Down
30 changes: 30 additions & 0 deletions packages/ember-runtime/tests/system/object/observer_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,3 +199,33 @@ testBoth('chain observer on class', function(get, set) {
equal(get(obj1, 'count'), 1, 'should not invoke again');
equal(get(obj2, 'count'), 1, 'should invoke observer on obj2');
});

testBoth('chain observer on class that has a reference to an uninitialized object will finish chains that reference it', function(get, set) {
var changed = false;

var ChildClass = EmberObject.extend({
parent: null,
parentOneTwoDidChange: observer('parent.one.two', function() {
changed = true;
})
});

var ParentClass = EmberObject.extend({
one: {
two: "old"
},
init: function () {
this.child = ChildClass.create({
parent: this
});
}
});

var parent = new ParentClass();

equal(changed, false, 'precond');

parent.set('one.two', 'new');

equal(changed, true, 'child should have been notified of change to path');
});