-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[PERF beta] @each
should remain a stable node for chains.
#11990
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,7 +26,7 @@ import { | |
sendEvent, | ||
hasListeners | ||
} from 'ember-metal/events'; | ||
import { isWatching } from 'ember-metal/watching'; | ||
import EachProxy from 'ember-runtime/system/each_proxy'; | ||
|
||
function arrayObserversHelper(obj, target, opts, operation, notify) { | ||
var willChange = (opts && opts.willChange) || 'arrayWillChange'; | ||
|
@@ -422,13 +422,13 @@ export default Mixin.create(Enumerable, { | |
} | ||
} | ||
|
||
// Make sure the @each proxy is set up if anyone is observing @each | ||
if (isWatching(this, '@each')) { | ||
get(this, '@each'); | ||
if (this.__each) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. where is __each set? Is it non-enumerable? |
||
this.__each.arrayWillChange(this, startIdx, removeAmt, addAmt); | ||
} | ||
|
||
sendEvent(this, '@array:before', [this, startIdx, removeAmt, addAmt]); | ||
|
||
|
||
if (startIdx >= 0 && removeAmt >= 0 && get(this, 'hasEnumerableObservers')) { | ||
removing = []; | ||
lim = startIdx + removeAmt; | ||
|
@@ -489,6 +489,11 @@ export default Mixin.create(Enumerable, { | |
} | ||
|
||
this.enumerableContentDidChange(removeAmt, adding); | ||
|
||
if (this.__each) { | ||
this.__each.arrayDidChange(this, startIdx, removeAmt, addAmt); | ||
} | ||
|
||
sendEvent(this, '@array:change', [this, startIdx, removeAmt, addAmt]); | ||
|
||
var length = get(this, 'length'); | ||
|
@@ -508,10 +513,6 @@ export default Mixin.create(Enumerable, { | |
return this; | ||
}, | ||
|
||
// .......................................................... | ||
// ENUMERATED PROPERTIES | ||
// | ||
|
||
/** | ||
Returns a special object that can be used to observe individual properties | ||
on the array. Just get an equivalent property on this object and it will | ||
|
@@ -525,13 +526,11 @@ export default Mixin.create(Enumerable, { | |
@public | ||
*/ | ||
'@each': computed(function() { | ||
// TODO use Symbol or add to meta | ||
if (!this.__each) { | ||
// ES6TODO: GRRRRR | ||
var EachProxy = requireModule('ember-runtime/system/each_proxy')['EachProxy']; | ||
|
||
this.__each = new EachProxy(this); | ||
} | ||
|
||
return this.__each; | ||
}) | ||
}).volatile() | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,194 +1,72 @@ | ||
/** | ||
@module ember | ||
@submodule ember-runtime | ||
*/ | ||
|
||
import Ember from 'ember-metal/core'; // Ember.assert | ||
|
||
import { get } from 'ember-metal/property_get'; | ||
import { guidFor } from 'ember-metal/utils'; | ||
import { typeOf } from 'ember-runtime/utils'; | ||
import EmberArray from 'ember-runtime/mixins/array'; // ES6TODO: WAT? Circular dep? | ||
import EmberObject from 'ember-runtime/system/object'; | ||
import { computed } from 'ember-metal/computed'; | ||
import { | ||
addObserver, | ||
_addBeforeObserver, | ||
_removeBeforeObserver, | ||
addObserver, | ||
removeObserver | ||
} from 'ember-metal/observer'; | ||
import { watchedEvents } from 'ember-metal/events'; | ||
import { defineProperty } from 'ember-metal/properties'; | ||
import { | ||
beginPropertyChanges, | ||
propertyDidChange, | ||
propertyWillChange, | ||
endPropertyChanges, | ||
changeProperties | ||
propertyWillChange | ||
} from 'ember-metal/property_events'; | ||
|
||
var EachArray = EmberObject.extend(EmberArray, { | ||
|
||
init(content, keyName, owner) { | ||
this._super(...arguments); | ||
this._keyName = keyName; | ||
this._owner = owner; | ||
this._content = content; | ||
}, | ||
|
||
objectAt(idx) { | ||
var item = this._content.objectAt(idx); | ||
return item && get(item, this._keyName); | ||
}, | ||
|
||
length: computed(function() { | ||
var content = this._content; | ||
return content ? get(content, 'length') : 0; | ||
}) | ||
|
||
}); | ||
|
||
var IS_OBSERVER = /^.+:(before|change)$/; | ||
|
||
function addObserverForContentKey(content, keyName, proxy, idx, loc) { | ||
var objects = proxy._objects; | ||
var guid; | ||
if (!objects) { | ||
objects = proxy._objects = {}; | ||
} | ||
|
||
while (--loc >= idx) { | ||
var item = content.objectAt(loc); | ||
if (item) { | ||
Ember.assert('When using @each to observe the array ' + content + ', the array must return an object', typeOf(item) === 'instance' || typeOf(item) === 'object'); | ||
_addBeforeObserver(item, keyName, proxy, 'contentKeyWillChange'); | ||
addObserver(item, keyName, proxy, 'contentKeyDidChange'); | ||
|
||
// keep track of the index each item was found at so we can map | ||
// it back when the obj changes. | ||
guid = guidFor(item); | ||
if (!objects[guid]) { | ||
objects[guid] = []; | ||
} | ||
|
||
objects[guid].push(loc); | ||
} | ||
} | ||
} | ||
|
||
function removeObserverForContentKey(content, keyName, proxy, idx, loc) { | ||
var objects = proxy._objects; | ||
if (!objects) { | ||
objects = proxy._objects = {}; | ||
} | ||
|
||
var indices, guid; | ||
|
||
while (--loc >= idx) { | ||
var item = content.objectAt(loc); | ||
if (item) { | ||
_removeBeforeObserver(item, keyName, proxy, 'contentKeyWillChange'); | ||
removeObserver(item, keyName, proxy, 'contentKeyDidChange'); | ||
|
||
guid = guidFor(item); | ||
indices = objects[guid]; | ||
indices[indices.indexOf(loc)] = null; | ||
} | ||
} | ||
} | ||
|
||
/** | ||
This is the object instance returned when you get the `@each` property on an | ||
array. It uses the unknownProperty handler to automatically create | ||
EachArray instances for property names. | ||
@class EachProxy | ||
@private | ||
*/ | ||
var EachProxy = EmberObject.extend({ | ||
|
||
init(content) { | ||
this._super(...arguments); | ||
this._content = content; | ||
content.addArrayObserver(this); | ||
|
||
// in case someone is already observing some keys make sure they are | ||
// added | ||
watchedEvents(this).forEach((eventName) => { | ||
this.didAddListener(eventName); | ||
}); | ||
}, | ||
|
||
/** | ||
You can directly access mapped properties by simply requesting them. | ||
The `unknownProperty` handler will generate an EachArray of each item. | ||
|
||
@method unknownProperty | ||
@param keyName {String} | ||
@param value {*} | ||
@private | ||
*/ | ||
unknownProperty(keyName, value) { | ||
var ret = new EachArray(this._content, keyName, this); | ||
defineProperty(this, keyName, null, ret); | ||
this.beginObservingContentKey(keyName); | ||
return ret; | ||
}, | ||
function EachProxy(content) { | ||
this._content = content; | ||
this._keys = undefined; | ||
this.__ember_meta__ = undefined; | ||
} | ||
|
||
EachProxy.prototype = { | ||
// .......................................................... | ||
// ARRAY CHANGES | ||
// Invokes whenever the content array itself changes. | ||
|
||
arrayWillChange(content, idx, removedCnt, addedCnt) { | ||
var keys = this._keys; | ||
var key, lim; | ||
|
||
lim = removedCnt>0 ? idx+removedCnt : -1; | ||
beginPropertyChanges(this); | ||
|
||
for (key in keys) { | ||
if (!keys.hasOwnProperty(key)) { continue; } | ||
|
||
if (lim>0) { removeObserverForContentKey(content, key, this, idx, lim); } | ||
|
||
let keys = this._keys; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. a here it is: https://github.com/emberjs/ember.js/blob/fix-each-beta/packages/ember-runtime/lib/system/each_proxy.js#L79 ya this should be one of those There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is a empty, this is beta though |
||
let lim = removedCnt > 0 ? idx + removedCnt : -1; | ||
for (let key in keys) { | ||
if (!keys.hasOwnProperty(key)) { | ||
continue; | ||
} | ||
if (lim>0) { | ||
removeObserverForContentKey(content, key, this, idx, lim); | ||
} | ||
propertyWillChange(this, key); | ||
} | ||
|
||
propertyWillChange(this._content, '@each'); | ||
endPropertyChanges(this); | ||
}, | ||
|
||
arrayDidChange(content, idx, removedCnt, addedCnt) { | ||
var keys = this._keys; | ||
var lim; | ||
|
||
lim = addedCnt>0 ? idx+addedCnt : -1; | ||
changeProperties(function() { | ||
for (var key in keys) { | ||
if (!keys.hasOwnProperty(key)) { continue; } | ||
|
||
if (lim>0) { addObserverForContentKey(content, key, this, idx, lim); } | ||
|
||
propertyDidChange(this, key); | ||
let keys = this._keys; | ||
let lim = addedCnt>0 ? idx+addedCnt : -1; | ||
for (let key in keys) { | ||
if (!keys.hasOwnProperty(key)) { | ||
continue; | ||
} | ||
|
||
propertyDidChange(this._content, '@each'); | ||
}, this); | ||
if (lim>0) { | ||
addObserverForContentKey(content, key, this, idx, lim); | ||
} | ||
propertyDidChange(this, key); | ||
} | ||
}, | ||
|
||
// .......................................................... | ||
// LISTEN FOR NEW OBSERVERS AND OTHER EVENT LISTENERS | ||
// Start monitoring keys based on who is listening... | ||
|
||
didAddListener(eventName) { | ||
if (IS_OBSERVER.test(eventName)) { | ||
this.beginObservingContentKey(eventName.slice(0, -7)); | ||
} | ||
willWatchProperty(property) { | ||
this.beginObservingContentKey(property); | ||
}, | ||
|
||
didRemoveListener(eventName) { | ||
if (IS_OBSERVER.test(eventName)) { | ||
this.stopObservingContentKey(eventName.slice(0, -7)); | ||
} | ||
didUnwatchProperty(property) { | ||
this.stopObservingContentKey(property); | ||
}, | ||
|
||
// .......................................................... | ||
|
@@ -229,9 +107,27 @@ var EachProxy = EmberObject.extend({ | |
contentKeyDidChange(obj, keyName) { | ||
propertyDidChange(this, keyName); | ||
} | ||
}); | ||
|
||
export { | ||
EachArray, | ||
EachProxy | ||
}; | ||
|
||
function addObserverForContentKey(content, keyName, proxy, idx, loc) { | ||
while (--loc >= idx) { | ||
var item = content.objectAt(loc); | ||
if (item) { | ||
Ember.assert('When using @each to observe the array ' + content + ', the array must return an object', typeof item === 'object'); | ||
_addBeforeObserver(item, keyName, proxy, 'contentKeyWillChange'); | ||
addObserver(item, keyName, proxy, 'contentKeyDidChange'); | ||
} | ||
} | ||
} | ||
|
||
function removeObserverForContentKey(content, keyName, proxy, idx, loc) { | ||
while (--loc >= idx) { | ||
var item = content.objectAt(loc); | ||
if (item) { | ||
_removeBeforeObserver(item, keyName, proxy, 'contentKeyWillChange'); | ||
removeObserver(item, keyName, proxy, 'contentKeyDidChange'); | ||
} | ||
} | ||
} | ||
|
||
export default EachProxy; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this can be export default function EachProxy (...) { .. }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
funky whitespace