Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

Commit

Permalink
fix(ngRepeat): now works with primitive types
Browse files Browse the repository at this point in the history
closes #933
  • Loading branch information
zhenbzha authored and mhevery committed Sep 6, 2012
1 parent 42c38b2 commit f2b7fff
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 1 deletion.
10 changes: 10 additions & 0 deletions src/apis.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,5 +97,15 @@ HashQueueMap.prototype = {
return array.shift();
}
}
},

/**
* return the first item without deleting it
*/
peek: function(key) {
var array = this[key = hashKey(key)];
if (array) {
return array[0];
}
}
};
28 changes: 27 additions & 1 deletion src/ng/directive/ngRepeat.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ var ngRepeatDirective = ngDirective({
// We need an array of these objects since the same object can be returned from the iterator.
// We expect this to be a rare case.
var lastOrder = new HashQueueMap();
var indexValues = [];
scope.$watch(function(scope){
var index, length,
collection = scope.$eval(rhs),
Expand Down Expand Up @@ -117,7 +118,20 @@ var ngRepeatDirective = ngDirective({
for (index = 0, length = array.length; index < length; index++) {
key = (collection === array) ? index : array[index];
value = collection[key];
last = lastOrder.shift(value);

// if collection is array and value is object, it can be shifted to allow for position change
// if collection is array and value is not object, need to first check whether index is same to
// avoid shifting wrong value
// if collection is not array, need to always check index to avoid shifting wrong value
if (lastOrder.peek(value)) {
last = collection === array ?
((isObject(value)) ? lastOrder.shift(value) :
(index === lastOrder.peek(value).index ? lastOrder.shift(value) : undefined)) :
(index === lastOrder.peek(value).index ? lastOrder.shift(value) : undefined);
} else {
last = undefined;
}

if (last) {
// if we have already seen this object, then we need to reuse the
// associated scope/element
Expand All @@ -137,6 +151,12 @@ var ngRepeatDirective = ngDirective({
cursor = last.element;
}
} else {
if (indexValues.hasOwnProperty(index) && collection !== array) {
var preValue = indexValues[index];
var v = lastOrder.shift(preValue);
v.element.remove();
v.scope.$destroy();
}
// new item which we don't know about
childScope = scope.$new();
}
Expand All @@ -158,10 +178,16 @@ var ngRepeatDirective = ngDirective({
index: index
};
nextOrder.push(value, last);
indexValues[index] = value;
});
}
}

var i, l;
for (i = 0, l = indexValues.length - length; i < l; i++) {
indexValues.pop();
}

//shrink children
for (key in lastOrder) {
if (lastOrder.hasOwnProperty(key)) {
Expand Down
4 changes: 4 additions & 0 deletions test/ApiSpecs.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ describe('api', function() {
map.push('key', 'a');
map.push('key', 'b');
expect(map[hashKey('key')]).toEqual(['a', 'b']);
expect(map.peek('key')).toEqual('a');
expect(map[hashKey('key')]).toEqual(['a', 'b']);
expect(map.shift('key')).toEqual('a');
expect(map.peek('key')).toEqual('b');
expect(map[hashKey('key')]).toEqual(['b']);
expect(map.shift('key')).toEqual('b');
expect(map.shift('key')).toEqual(undefined);
expect(map[hashKey('key')]).toEqual(undefined);
Expand Down
115 changes: 115 additions & 0 deletions test/ng/directive/ngRepeatSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,89 @@ describe('ngRepeat', function() {
}));


it('should ngRepeat over array of primitive correctly', inject(function($rootScope, $compile) {
element = $compile(
'<ul>' +
'<li ng-repeat="item in items" ng-init="suffix = \';\'" ng-bind="item + suffix"></li>' +
'</ul>')($rootScope);

Array.prototype.extraProperty = "should be ignored";
// INIT
$rootScope.items = [true, true, true];
$rootScope.$digest();
expect(element.find('li').length).toEqual(3);
expect(element.text()).toEqual('true;true;true;');
delete Array.prototype.extraProperty;

$rootScope.items = [false, true, true];
$rootScope.$digest();
expect(element.find('li').length).toEqual(3);
expect(element.text()).toEqual('false;true;true;');

$rootScope.items = [false, true, false];
$rootScope.$digest();
expect(element.find('li').length).toEqual(3);
expect(element.text()).toEqual('false;true;false;');

$rootScope.items = [true];
$rootScope.$digest();
expect(element.find('li').length).toEqual(1);
expect(element.text()).toEqual('true;');

$rootScope.items = [true, true, false];
$rootScope.$digest();
expect(element.find('li').length).toEqual(3);
expect(element.text()).toEqual('true;true;false;');

$rootScope.items = [true, false, false];
$rootScope.$digest();
expect(element.find('li').length).toEqual(3);
expect(element.text()).toEqual('true;false;false;');

// string
$rootScope.items = ['a', 'a', 'a'];
$rootScope.$digest();
expect(element.find('li').length).toEqual(3);
expect(element.text()).toEqual('a;a;a;');

$rootScope.items = ['ab', 'a', 'a'];
$rootScope.$digest();
expect(element.find('li').length).toEqual(3);
expect(element.text()).toEqual('ab;a;a;');

$rootScope.items = ['test'];
$rootScope.$digest();
expect(element.find('li').length).toEqual(1);
expect(element.text()).toEqual('test;');

$rootScope.items = ['same', 'value'];
$rootScope.$digest();
expect(element.find('li').length).toEqual(2);
expect(element.text()).toEqual('same;value;');

// number
$rootScope.items = [12, 12, 12];
$rootScope.$digest();
expect(element.find('li').length).toEqual(3);
expect(element.text()).toEqual('12;12;12;');

$rootScope.items = [53, 12, 27];
$rootScope.$digest();
expect(element.find('li').length).toEqual(3);
expect(element.text()).toEqual('53;12;27;');

$rootScope.items = [89];
$rootScope.$digest();
expect(element.find('li').length).toEqual(1);
expect(element.text()).toEqual('89;');

$rootScope.items = [89, 23];
$rootScope.$digest();
expect(element.find('li').length).toEqual(2);
expect(element.text()).toEqual('89;23;');
}));


it('should ngRepeat over object', inject(function($rootScope, $compile) {
element = $compile(
'<ul>' +
Expand All @@ -47,6 +130,38 @@ describe('ngRepeat', function() {
expect(element.text()).toEqual('misko:swe;shyam:set;');
}));


it('should ngRepeat over object with primitive value correctly', inject(function($rootScope, $compile) {
element = $compile(
'<ul>' +
'<li ng-repeat="(key, value) in items" ng-bind="key + \':\' + value + \';\' "></li>' +
'</ul>')($rootScope);
$rootScope.items = {misko:'true', shyam:'true', zhenbo: 'true'};
$rootScope.$digest();
expect(element.find('li').length).toEqual(3);
expect(element.text()).toEqual('misko:true;shyam:true;zhenbo:true;');

$rootScope.items = {misko:'false', shyam:'true', zhenbo: 'true'};
$rootScope.$digest();
expect(element.find('li').length).toEqual(3);
expect(element.text()).toEqual('misko:false;shyam:true;zhenbo:true;');

$rootScope.items = {misko:'false', shyam:'false', zhenbo: 'false'};
$rootScope.$digest();
expect(element.find('li').length).toEqual(3);
expect(element.text()).toEqual('misko:false;shyam:false;zhenbo:false;');

$rootScope.items = {misko:'true'};
$rootScope.$digest();
expect(element.find('li').length).toEqual(1);
expect(element.text()).toEqual('misko:true;');

$rootScope.items = {shyam:'true', zhenbo: 'false'};
$rootScope.$digest();
expect(element.find('li').length).toEqual(2);
expect(element.text()).toEqual('shyam:true;zhenbo:false;');
}));


it('should not ngRepeat over parent properties', inject(function($rootScope, $compile) {
var Class = function() {};
Expand Down

0 comments on commit f2b7fff

Please sign in to comment.