From 31c56f540045b5270f5b8e235873da855caf3486 Mon Sep 17 00:00:00 2001 From: jankuca Date: Tue, 24 Sep 2013 13:51:28 -0700 Subject: [PATCH] fix(ngRepeat): correctly track elements even when the collection is initially undefined Previously if the collection model was set to undefined on the first digest, the repeater would get confused and not use the correct tracking function for associating model with dom elements in the repeater. Closes #4145 Closes #3964 --- src/ng/directive/ngRepeat.js | 10 ++++++---- test/ng/directive/ngRepeatSpec.js | 22 ++++++++++++++++++++-- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/ng/directive/ngRepeat.js b/src/ng/directive/ngRepeat.js index 50be2a618e68..16a810ef1133 100644 --- a/src/ng/directive/ngRepeat.js +++ b/src/ng/directive/ngRepeat.js @@ -218,7 +218,8 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) { return function($scope, $element, $attr){ var expression = $attr.ngRepeat; var match = expression.match(/^\s*(.+)\s+in\s+(.*?)\s*(\s+track\s+by\s+(.+)\s*)?$/), - trackByExp, trackByExpGetter, trackByIdFn, trackByIdArrayFn, trackByIdObjFn, lhs, rhs, valueIdentifier, keyIdentifier, + trackByExp, trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn, + lhs, rhs, valueIdentifier, keyIdentifier, hashFnLocals = {$id: hashKey}; if (!match) { @@ -232,7 +233,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) { if (trackByExp) { trackByExpGetter = $parse(trackByExp); - trackByIdFn = function(key, value, index) { + trackByIdExpFn = function(key, value, index) { // assign key, value, and $index to the locals so that they can be used in hash functions if (keyIdentifier) hashFnLocals[keyIdentifier] = key; hashFnLocals[valueIdentifier] = value; @@ -275,6 +276,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) { childScope, key, value, // key/value of iteration trackById, + trackByIdFn, collectionKeys, block, // last object information {scope, element, id} nextBlockOrder = []; @@ -282,9 +284,9 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) { if (isArrayLike(collection)) { collectionKeys = collection; - trackByIdFn = trackByIdFn || trackByIdArrayFn; + trackByIdFn = trackByIdExpFn || trackByIdArrayFn; } else { - trackByIdFn = trackByIdFn || trackByIdObjFn; + trackByIdFn = trackByIdExpFn || trackByIdObjFn; // if object, extract keys, sort them and use to determine order of iteration over obj props collectionKeys = []; for (key in collection) { diff --git a/test/ng/directive/ngRepeatSpec.js b/test/ng/directive/ngRepeatSpec.js index fc41bc6d71eb..720355666458 100644 --- a/test/ng/directive/ngRepeatSpec.js +++ b/test/ng/directive/ngRepeatSpec.js @@ -841,6 +841,26 @@ describe('ngRepeat', function() { expect(newLis[1]).toEqual(lis[0]); expect(newLis[2]).toEqual(lis[1]); }); + + it('should be stable even if the collection is initially undefined', function () { + scope.items = undefined; + scope.$digest(); + + scope.items = [ + { name: 'A' }, + { name: 'B' }, + { name: 'C' } + ]; + scope.$digest(); + + lis = element.find('li'); + scope.items.shift(); + scope.$digest(); + + var newLis = element.find('li'); + expect(newLis.length).toBe(2); + expect(newLis[0]).toBe(lis[1]); + }); }); it('should grow multi-node repeater', inject(function($compile, $rootScope) { @@ -861,8 +881,6 @@ describe('ngRepeat', function() { $rootScope.$digest(); expect(element.text()).toEqual('T1:D1;T2:D2;T3:D3;'); })); - - }); describe('ngRepeat animations', function() {