fix(angular): do not copy $$hashKey in copy/extend functions.

Copying the $$hashKey as part of copy/extend operations makes little
sense since hashkey is used primarily as an object id, especially in
the context of the ngRepeat directive. This change maintains the
existing $$hashKey of an object that is being copied into (likewise for
extend).
It is not uncommon to take an item in a collection, copy it,
and then append it to the collection. By copying the $$hashKey, this
leads to duplicate object errors with the current ngRepeat.

Closes #1875
This commit is contained in:
R. Merkert 2013-04-16 20:09:43 -04:00 committed by Pete Bacon Darwin
parent 1240641f76
commit 016e1e675e
2 changed files with 77 additions and 0 deletions

View file

@ -195,6 +195,21 @@ function nextUid() {
return uid.join(''); return uid.join('');
} }
/**
* Set or clear the hashkey for an object.
* @param obj object
* @param h the hashkey (!truthy to delete the hashkey)
*/
function setHashKey(obj, h) {
if (h) {
obj.$$hashKey = h;
}
else {
delete obj.$$hashKey;
}
}
/** /**
* @ngdoc function * @ngdoc function
* @name angular.extend * @name angular.extend
@ -208,6 +223,7 @@ function nextUid() {
* @param {...Object} src Source object(s). * @param {...Object} src Source object(s).
*/ */
function extend(dst) { function extend(dst) {
var h = dst.$$hashKey;
forEach(arguments, function(obj){ forEach(arguments, function(obj){
if (obj !== dst) { if (obj !== dst) {
forEach(obj, function(value, key){ forEach(obj, function(value, key){
@ -215,6 +231,8 @@ function extend(dst) {
}); });
} }
}); });
setHashKey(dst,h);
return dst; return dst;
} }
@ -569,12 +587,14 @@ function copy(source, destination){
destination.push(copy(source[i])); destination.push(copy(source[i]));
} }
} else { } else {
var h = destination.$$hashKey;
forEach(destination, function(value, key){ forEach(destination, function(value, key){
delete destination[key]; delete destination[key];
}); });
for ( var key in source) { for ( var key in source) {
destination[key] = copy(source[key]); destination[key] = copy(source[key]);
} }
setHashKey(destination,h);
} }
} }
return destination; return destination;

View file

@ -98,6 +98,63 @@ describe('angular', function() {
src = dst = [2, 4]; src = dst = [2, 4];
expect(function() { copy(src, dst); }).toThrow("Can't copy equivalent objects or arrays"); expect(function() { copy(src, dst); }).toThrow("Can't copy equivalent objects or arrays");
}); });
it('should not copy the private $$hashKey', function() {
var src,dst;
src = {};
hashKey(src);
dst = copy(src);
expect(hashKey(dst)).not.toEqual(hashKey(src));
});
it('should retain the previous $$hashKey', function() {
var src,dst,h;
src = {};
dst = {};
// force creation of a hashkey
h = hashKey(dst);
hashKey(src);
dst = copy(src,dst);
// make sure we don't copy the key
expect(hashKey(dst)).not.toEqual(hashKey(src));
// make sure we retain the old key
expect(hashKey(dst)).toEqual(h);
});
});
describe("extend", function() {
it('should not copy the private $$hashKey', function() {
var src,dst;
src = {};
dst = {};
hashKey(src);
dst = extend(dst,src);
expect(hashKey(dst)).not.toEqual(hashKey(src));
});
it('should retain the previous $$hashKey', function() {
var src,dst,h;
src = {};
dst = {};
h = hashKey(dst);
hashKey(src);
dst = extend(dst,src);
// make sure we don't copy the key
expect(hashKey(dst)).not.toEqual(hashKey(src));
// make sure we retain the old key
expect(hashKey(dst)).toEqual(h);
});
it('should work when extending with itself', function() {
var src,dst,h;
dst = src = {};
h = hashKey(dst);
dst = extend(dst,src);
// make sure we retain the old key
expect(hashKey(dst)).toEqual(h);
});
}); });
describe('shallow copy', function() { describe('shallow copy', function() {