mirror of
https://github.com/zhigang1992/angular.js.git
synced 2026-04-24 03:55:49 +08:00
fix(Scope): $watchCollection should call listener with oldValue
Originally we destroyed the oldValue by incrementaly copying over portions of the newValue into the oldValue during dirty-checking, this resulted in oldValue to be equal to newValue by the time we called the watchCollection listener. The fix creates a copy of the newValue each time a change is detected and then uses that copy *the next time* a change is detected. To make `$watchCollection` behave the same way as `$watch`, during the first iteration the listener is called with newValue and oldValue being identical. Since many of the corner-cases are already covered by existing tests, I refactored the test logging to include oldValue and made the tests more readable. Closes #2621 Closes #5661 Closes #5688 Closes #6736
This commit is contained in:
@@ -398,30 +398,40 @@ function $RootScopeProvider(){
|
||||
* {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
|
||||
* collection will trigger a call to the `listener`.
|
||||
*
|
||||
* @param {function(newCollection, oldCollection, scope)} listener a callback function that is
|
||||
* fired with both the `newCollection` and `oldCollection` as parameters.
|
||||
* The `newCollection` object is the newly modified data obtained from the `obj` expression
|
||||
* and the `oldCollection` object is a copy of the former collection data.
|
||||
* The `scope` refers to the current scope.
|
||||
* @param {function(newCollection, oldCollection, scope)} listener a callback function called
|
||||
* when a change is detected.
|
||||
* - The `newCollection` object is the newly modified data obtained from the `obj` expression
|
||||
* - The `oldCollection` object is a copy of the former collection data.
|
||||
* Due to performance considerations, the`oldCollection` value is computed only if the
|
||||
* `listener` function declares two or more arguments.
|
||||
* - The `scope` argument refers to the current scope.
|
||||
*
|
||||
* @returns {function()} Returns a de-registration function for this listener. When the
|
||||
* de-registration function is executed, the internal watch operation is terminated.
|
||||
*/
|
||||
$watchCollection: function(obj, listener) {
|
||||
var self = this;
|
||||
var oldValue;
|
||||
// the current value, updated on each dirty-check run
|
||||
var newValue;
|
||||
// a shallow copy of the newValue from the last dirty-check run,
|
||||
// updated to match newValue during dirty-check run
|
||||
var oldValue;
|
||||
// a shallow copy of the newValue from when the last change happened
|
||||
var veryOldValue;
|
||||
// only track veryOldValue if the listener is asking for it
|
||||
var trackVeryOldValue = (listener.length > 1);
|
||||
var changeDetected = 0;
|
||||
var objGetter = $parse(obj);
|
||||
var internalArray = [];
|
||||
var internalObject = {};
|
||||
var initRun = true;
|
||||
var oldLength = 0;
|
||||
|
||||
function $watchCollectionWatch() {
|
||||
newValue = objGetter(self);
|
||||
var newLength, key;
|
||||
|
||||
if (!isObject(newValue)) {
|
||||
if (!isObject(newValue)) { // if primitive
|
||||
if (oldValue !== newValue) {
|
||||
oldValue = newValue;
|
||||
changeDetected++;
|
||||
@@ -487,7 +497,32 @@ function $RootScopeProvider(){
|
||||
}
|
||||
|
||||
function $watchCollectionAction() {
|
||||
listener(newValue, oldValue, self);
|
||||
if (initRun) {
|
||||
initRun = false;
|
||||
listener(newValue, newValue, self);
|
||||
} else {
|
||||
listener(newValue, veryOldValue, self);
|
||||
}
|
||||
|
||||
// make a copy for the next time a collection is changed
|
||||
if (trackVeryOldValue) {
|
||||
if (!isObject(newValue)) {
|
||||
//primitive
|
||||
veryOldValue = newValue;
|
||||
} else if (isArrayLike(newValue)) {
|
||||
veryOldValue = new Array(newValue.length);
|
||||
for (var i = 0; i < newValue.length; i++) {
|
||||
veryOldValue[i] = newValue[i];
|
||||
}
|
||||
} else { // if object
|
||||
veryOldValue = {};
|
||||
for (var key in newValue) {
|
||||
if (hasOwnProperty.call(newValue, key)) {
|
||||
veryOldValue[key] = newValue[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.$watch($watchCollectionWatch, $watchCollectionAction);
|
||||
|
||||
Reference in New Issue
Block a user